@universis/janitor 1.0.0 → 1.0.1

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.
Files changed (38) hide show
  1. package/README.md +1 -1
  2. package/dist/RateLimitService.js +20 -1
  3. package/dist/RateLimitService.js.map +1 -1
  4. package/dist/RedisClientStore.d.ts +5 -0
  5. package/dist/RedisClientStore.js +98 -0
  6. package/dist/RedisClientStore.js.map +1 -0
  7. package/dist/ScopeAccessConfiguration.d.ts +60 -0
  8. package/dist/ScopeAccessConfiguration.js +93 -0
  9. package/dist/ScopeAccessConfiguration.js.map +1 -0
  10. package/dist/SpeedLimitService.js +20 -1
  11. package/dist/SpeedLimitService.js.map +1 -1
  12. package/dist/index.d.ts +3 -1
  13. package/dist/index.js +4 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/polyfills.js +11 -0
  16. package/dist/polyfills.js.map +1 -0
  17. package/package.json +4 -2
  18. package/public/favicon.ico +0 -0
  19. package/public/universis_janitor.svg +25 -0
  20. package/public/universis_janitor_128.png +0 -0
  21. package/public/universis_janitor_256.png +0 -0
  22. package/src/RateLimitService.js +20 -1
  23. package/src/RedisClientStore.d.ts +5 -0
  24. package/src/RedisClientStore.js +101 -0
  25. package/src/ScopeAccessConfiguration.d.ts +60 -0
  26. package/src/ScopeAccessConfiguration.js +97 -0
  27. package/src/SpeedLimitService.js +20 -1
  28. package/src/index.d.ts +3 -1
  29. package/src/index.js +4 -1
  30. package/src/polyfills.js +10 -0
  31. package/dist/models/TestAction.d.ts +0 -5
  32. package/dist/models/TestAction.js +0 -5
  33. package/dist/models/TestAction.js.map +0 -1
  34. package/public/janitor-logo-128.png +0 -0
  35. package/public/janitor-logo-text.png +0 -0
  36. package/public/janitor-logo.svg +0 -13
  37. package/src/models/TestAction.d.ts +0 -5
  38. package/src/models/TestAction.js +0 -10
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @universis/janitor
2
2
 
3
- ![@universis/janitor logo](./public/janitor-logo-text.png)
3
+ ![@universis/janitor logo](./public/universis_janitor_128.png)
4
4
 
5
5
  Universis api plugin for rate limiting requests or slowing down service responses.
6
6
 
@@ -50,9 +50,28 @@ class RateLimitService extends _common.ApplicationService {
50
50
  legacyHeaders: true // send headers
51
51
  }, profile, {
52
52
  keyGenerator: (req) => {
53
- return req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);
53
+ const ip = req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);
54
+ return `${path}:${ip}`;
54
55
  }
55
56
  });
57
+ if (typeof rateLimitOptions.store === 'string') {
58
+ // load store
59
+ const store = rateLimitOptions.store.split('#');
60
+ let StoreClass;
61
+ if (store.length === 2) {
62
+ const storeModule = require(store[0]);
63
+ if (Object.prototype.hasOwnProperty.call(storeModule, store[1])) {
64
+ StoreClass = storeModule[store[1]];
65
+ rateLimitOptions.store = new StoreClass(this, rateLimitOptions);
66
+ } else {
67
+ throw new Error(`${store} cannot be found or is inaccessible`);
68
+ }
69
+ } else {
70
+ StoreClass = require(store[0]);
71
+ // create store
72
+ rateLimitOptions.store = new StoreClass(this, rateLimitOptions);
73
+ }
74
+ }
56
75
  addRouter.use(path, (0, _expressRateLimit.rateLimit)(rateLimitOptions));
57
76
  }
58
77
  });
@@ -1 +1 @@
1
- {"version":3,"file":"RateLimitService.js","names":["_common","require","_expressRateLimit","_express","_interopRequireDefault","_path","obj","__esModule","default","RateLimitService","ApplicationService","constructor","app","serviceRouter","subscribe","addRouter","express","Router","serviceConfiguration","getConfiguration","getSourceAt","profiles","paths","extends","configurationPath","getConfigurationPath","extendsPath","path","resolve","TraceUtils","log","pathsArray","profilesArray","Map","size","warn","forEach","value","profile","get","rateLimitOptions","Object","assign","windowMs","limit","legacyHeaders","keyGenerator","req","headers","connection","remoteAddress","socket","use","rateLimit","stack","length","unshift","apply","err","error","exports"],"sources":["../src/RateLimitService.js"],"sourcesContent":["import { ApplicationService, TraceUtils } from '@themost/common';\nimport { rateLimit } from 'express-rate-limit';\nimport express from 'express';\nimport path from 'path';\n\nexport class RateLimitService extends ApplicationService {\n /**\n * @param {import('@themost/express').ExpressDataApplication} app \n */\n constructor(app) {\n super(app);\n app.serviceRouter.subscribe(serviceRouter => {\n if (serviceRouter == null) {\n return;\n }\n try {\n const addRouter = express.Router();\n let serviceConfiguration = app.getConfiguration().getSourceAt('settings/universis/janitor/rateLimit') || {\n profiles: [],\n paths: []\n };\n if (serviceConfiguration.extends) {\n // get additional configuration\n const configurationPath = app.getConfiguration().getConfigurationPath();\n const extendsPath = path.resolve(configurationPath, serviceConfiguration.extends);\n TraceUtils.log(`@universis/janitor#RateLimitService will try to extend service configuration from ${extendsPath}`);\n serviceConfiguration = require(extendsPath);\n }\n const pathsArray = serviceConfiguration.paths || [];\n const profilesArray = serviceConfiguration.profiles || [];\n // create maps\n const paths = new Map(pathsArray);\n const profiles = new Map(profilesArray);\n if (paths.size === 0) {\n TraceUtils.warn('@universis/janitor#RateLimitService is being started but the collection of paths is empty.');\n }\n paths.forEach((value, path) => {\n let profile;\n // get profile\n if (value.profile) {\n profile = profiles.get(value.profile);\n } else {\n // or options defined inline\n profile = value\n }\n if (profile != null) {\n const rateLimitOptions = Object.assign({\n windowMs: 5 * 60 * 1000, // 5 minutes\n limit: 50, // 50 requests\n legacyHeaders: true // send headers\n }, profile, {\n keyGenerator: (req) => {\n return req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);\n }\n });\n addRouter.use(path, rateLimit(rateLimitOptions));\n }\n });\n if (addRouter.stack.length) {\n serviceRouter.stack.unshift.apply(serviceRouter.stack, addRouter.stack);\n }\n } catch (err) {\n TraceUtils.error('An error occurred while validating rate limit configuration.');\n TraceUtils.error(err);\n TraceUtils.warn('Rate limit service is inactive due to an error occured while loading configuration.')\n }\n });\n }\n\n}"],"mappings":"6GAAA,IAAAA,OAAA,GAAAC,OAAA;AACA,IAAAC,iBAAA,GAAAD,OAAA;AACA,IAAAE,QAAA,GAAAC,sBAAA,CAAAH,OAAA;AACA,IAAAI,KAAA,GAAAD,sBAAA,CAAAH,OAAA,UAAwB,SAAAG,uBAAAE,GAAA,UAAAA,GAAA,IAAAA,GAAA,CAAAC,UAAA,GAAAD,GAAA,KAAAE,OAAA,EAAAF,GAAA;;AAEjB,MAAMG,gBAAgB,SAASC,0BAAkB,CAAC;EACrD;AACJ;AACA;EACIC,WAAWA,CAACC,GAAG,EAAE;IACb,KAAK,CAACA,GAAG,CAAC;IACVA,GAAG,CAACC,aAAa,CAACC,SAAS,CAAC,CAAAD,aAAa,KAAI;MACzC,IAAIA,aAAa,IAAI,IAAI,EAAE;QACvB;MACJ;MACA,IAAI;QACA,MAAME,SAAS,GAAGC,gBAAO,CAACC,MAAM,EAAE;QAClC,IAAIC,oBAAoB,GAAGN,GAAG,CAACO,gBAAgB,EAAE,CAACC,WAAW,CAAC,sCAAsC,CAAC,IAAI;UACrGC,QAAQ,EAAE,EAAE;UACZC,KAAK,EAAE;QACX,CAAC;QACD,IAAIJ,oBAAoB,CAACK,OAAO,EAAE;UAC9B;UACA,MAAMC,iBAAiB,GAAGZ,GAAG,CAACO,gBAAgB,EAAE,CAACM,oBAAoB,EAAE;UACvE,MAAMC,WAAW,GAAGC,aAAI,CAACC,OAAO,CAACJ,iBAAiB,EAAEN,oBAAoB,CAACK,OAAO,CAAC;UACjFM,kBAAU,CAACC,GAAG,CAAE,qFAAoFJ,WAAY,EAAC,CAAC;UAClHR,oBAAoB,GAAGjB,OAAO,CAACyB,WAAW,CAAC;QAC/C;QACA,MAAMK,UAAU,GAAGb,oBAAoB,CAACI,KAAK,IAAI,EAAE;QACnD,MAAMU,aAAa,GAAGd,oBAAoB,CAACG,QAAQ,IAAI,EAAE;QACzD;QACA,MAAMC,KAAK,GAAG,IAAIW,GAAG,CAACF,UAAU,CAAC;QACjC,MAAMV,QAAQ,GAAG,IAAIY,GAAG,CAACD,aAAa,CAAC;QACvC,IAAIV,KAAK,CAACY,IAAI,KAAK,CAAC,EAAE;UAClBL,kBAAU,CAACM,IAAI,CAAC,4FAA4F,CAAC;QACjH;QACAb,KAAK,CAACc,OAAO,CAAC,CAACC,KAAK,EAAEV,IAAI,KAAK;UAC3B,IAAIW,OAAO;UACX;UACA,IAAID,KAAK,CAACC,OAAO,EAAE;YACfA,OAAO,GAAGjB,QAAQ,CAACkB,GAAG,CAACF,KAAK,CAACC,OAAO,CAAC;UACzC,CAAC,MAAM;YACH;YACAA,OAAO,GAAGD,KAAK;UACnB;UACA,IAAIC,OAAO,IAAI,IAAI,EAAE;YACjB,MAAME,gBAAgB,GAAGC,MAAM,CAACC,MAAM,CAAC;cACnCC,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE;cACzBC,KAAK,EAAE,EAAE,EAAE;cACXC,aAAa,EAAE,IAAI,CAAC;YACxB,CAAC,EAAEP,OAAO,EAAE;cACRQ,YAAY,EAAEA,CAACC,GAAG,KAAK;gBACnB,OAAOA,GAAG,CAACC,OAAO,CAAC,WAAW,CAAC,IAAID,GAAG,CAACC,OAAO,CAAC,iBAAiB,CAAC,KAAKD,GAAG,CAACE,UAAU,GAAGF,GAAG,CAACE,UAAU,CAACC,aAAa,GAAGH,GAAG,CAACI,MAAM,CAACD,aAAa,CAAC;cACnJ;YACJ,CAAC,CAAC;YACFnC,SAAS,CAACqC,GAAG,CAACzB,IAAI,EAAE,IAAA0B,2BAAS,EAACb,gBAAgB,CAAC,CAAC;UACpD;QACJ,CAAC,CAAC;QACF,IAAIzB,SAAS,CAACuC,KAAK,CAACC,MAAM,EAAE;UACxB1C,aAAa,CAACyC,KAAK,CAACE,OAAO,CAACC,KAAK,CAAC5C,aAAa,CAACyC,KAAK,EAAEvC,SAAS,CAACuC,KAAK,CAAC;QAC3E;MACJ,CAAC,CAAC,OAAOI,GAAG,EAAE;QACV7B,kBAAU,CAAC8B,KAAK,CAAC,8DAA8D,CAAC;QAChF9B,kBAAU,CAAC8B,KAAK,CAACD,GAAG,CAAC;QACrB7B,kBAAU,CAACM,IAAI,CAAC,qFAAqF,CAAC;MAC1G;IACJ,CAAC,CAAC;EACN;;AAEJ,CAACyB,OAAA,CAAAnD,gBAAA,GAAAA,gBAAA"}
1
+ {"version":3,"file":"RateLimitService.js","names":["_common","require","_expressRateLimit","_express","_interopRequireDefault","_path","obj","__esModule","default","RateLimitService","ApplicationService","constructor","app","serviceRouter","subscribe","addRouter","express","Router","serviceConfiguration","getConfiguration","getSourceAt","profiles","paths","extends","configurationPath","getConfigurationPath","extendsPath","path","resolve","TraceUtils","log","pathsArray","profilesArray","Map","size","warn","forEach","value","profile","get","rateLimitOptions","Object","assign","windowMs","limit","legacyHeaders","keyGenerator","req","ip","headers","connection","remoteAddress","socket","store","split","StoreClass","length","storeModule","prototype","hasOwnProperty","call","Error","use","rateLimit","stack","unshift","apply","err","error","exports"],"sources":["../src/RateLimitService.js"],"sourcesContent":["import { ApplicationService, TraceUtils } from '@themost/common';\nimport { rateLimit } from 'express-rate-limit';\nimport express from 'express';\nimport path from 'path';\n\nexport class RateLimitService extends ApplicationService {\n /**\n * @param {import('@themost/express').ExpressDataApplication} app \n */\n constructor(app) {\n super(app);\n app.serviceRouter.subscribe(serviceRouter => {\n if (serviceRouter == null) {\n return;\n }\n try {\n const addRouter = express.Router();\n let serviceConfiguration = app.getConfiguration().getSourceAt('settings/universis/janitor/rateLimit') || {\n profiles: [],\n paths: []\n };\n if (serviceConfiguration.extends) {\n // get additional configuration\n const configurationPath = app.getConfiguration().getConfigurationPath();\n const extendsPath = path.resolve(configurationPath, serviceConfiguration.extends);\n TraceUtils.log(`@universis/janitor#RateLimitService will try to extend service configuration from ${extendsPath}`);\n serviceConfiguration = require(extendsPath);\n }\n const pathsArray = serviceConfiguration.paths || [];\n const profilesArray = serviceConfiguration.profiles || [];\n // create maps\n const paths = new Map(pathsArray);\n const profiles = new Map(profilesArray);\n if (paths.size === 0) {\n TraceUtils.warn('@universis/janitor#RateLimitService is being started but the collection of paths is empty.');\n }\n paths.forEach((value, path) => {\n let profile;\n // get profile\n if (value.profile) {\n profile = profiles.get(value.profile);\n } else {\n // or options defined inline\n profile = value\n }\n if (profile != null) {\n const rateLimitOptions = Object.assign({\n windowMs: 5 * 60 * 1000, // 5 minutes\n limit: 50, // 50 requests\n legacyHeaders: true // send headers\n }, profile, {\n keyGenerator: (req) => {\n const ip = req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);\n return `${path}:${ip}`;\n }\n });\n if (typeof rateLimitOptions.store === 'string') {\n // load store\n const store = rateLimitOptions.store.split('#');\n let StoreClass;\n if (store.length === 2) {\n const storeModule = require(store[0]);\n if (Object.prototype.hasOwnProperty.call(storeModule, store[1])) {\n StoreClass = storeModule[store[1]];\n rateLimitOptions.store = new StoreClass(this, rateLimitOptions);\n } else {\n throw new Error(`${store} cannot be found or is inaccessible`);\n }\n } else {\n StoreClass = require(store[0]);\n // create store\n rateLimitOptions.store = new StoreClass(this, rateLimitOptions);\n }\n }\n addRouter.use(path, rateLimit(rateLimitOptions));\n }\n });\n if (addRouter.stack.length) {\n serviceRouter.stack.unshift.apply(serviceRouter.stack, addRouter.stack);\n }\n } catch (err) {\n TraceUtils.error('An error occurred while validating rate limit configuration.');\n TraceUtils.error(err);\n TraceUtils.warn('Rate limit service is inactive due to an error occured while loading configuration.')\n }\n });\n }\n\n}"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { ApplicationService } from '@themost/common';
2
+ import RedisStore from 'rate-limit-redis';
3
+ export declare class RedisClientStore extends RedisStore {
4
+ constructor(app: ApplicationService);
5
+ }
@@ -0,0 +1,98 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.RedisClientStore = void 0;var _common = require("@themost/common");
2
+ var _rateLimitRedis = _interopRequireDefault(require("rate-limit-redis"));
3
+ var _redis = require("redis");function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}function _defineProperty(obj, key, value) {key = _toPropertyKey(key);if (key in obj) {Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true });} else {obj[key] = value;}return obj;}function _toPropertyKey(arg) {var key = _toPrimitive(arg, "string");return typeof key === "symbol" ? key : String(key);}function _toPrimitive(input, hint) {if (typeof input !== "object" || input === null) return input;var prim = input[Symbol.toPrimitive];if (prim !== undefined) {var res = prim.call(input, hint || "default");if (typeof res !== "object") return res;throw new TypeError("@@toPrimitive must return a primitive value.");}return (hint === "string" ? String : Number)(input);}
4
+
5
+ const superLoadIncrementScript = _rateLimitRedis.default.prototype.loadIncrementScript;
6
+ const superLoadGetScript = _rateLimitRedis.default.prototype.loadGetScript;
7
+
8
+ function noLoadGetScript() {
9
+
10
+ //
11
+ }
12
+ function noLoadIncrementScript() {
13
+
14
+ //
15
+ }
16
+ if (superLoadIncrementScript != noLoadIncrementScript) {
17
+ _rateLimitRedis.default.prototype.loadIncrementScript = noLoadIncrementScript;
18
+ }
19
+
20
+ if (superLoadGetScript != noLoadGetScript) {
21
+ _rateLimitRedis.default.prototype.loadGetScript = noLoadGetScript;
22
+ }
23
+
24
+ class RedisClientStore extends _rateLimitRedis.default {
25
+
26
+
27
+
28
+
29
+
30
+
31
+ /**
32
+ *
33
+ * @param {import('@themost/common').ApplicationService} service
34
+ * @param {{windowMs: number}} options
35
+ */
36
+ constructor(service, options) {
37
+ super({
38
+ /**
39
+ * @param {...string} args
40
+ * @returns {Promise<*>}
41
+ */
42
+ sendCommand: function () {
43
+ const args = Array.from(arguments);
44
+ const self = this;
45
+ if (args[0] === 'SCRIPT') {
46
+ _common.TraceUtils.debug('RedisClientStore: Creating new client for sending script command');
47
+ const connectOptions = service.getApplication().getConfiguration().getSourceAt('settings/redis/options') || {
48
+ host: '127.0.0.1',
49
+ port: 6379
50
+ };
51
+ const client = (0, _redis.createClient)(connectOptions);
52
+ return client.connect().then(() => {
53
+ return client.sendCommand(args);
54
+ }).catch((error) => {
55
+ _common.TraceUtils.error(error);
56
+ return Promise.reject(error);
57
+ }).finally(() => {
58
+ if (client.isOpen) {
59
+ _common.TraceUtils.debug('RedisClientStore: Closing client of sending script command');
60
+ client.disconnect().catch((errDisconnect) => {
61
+ _common.TraceUtils.error(errDisconnect);
62
+ });
63
+ }
64
+ });
65
+ }
66
+ if (self.client == null) {
67
+ _common.TraceUtils.debug('RedisClientStore: Creating store client for sending commands');
68
+ const connectOptions = service.getApplication().getConfiguration().getSourceAt('settings/redis/options') || {
69
+ host: '127.0.0.1',
70
+ port: 6379
71
+ };
72
+ self.client = (0, _redis.createClient)(connectOptions);
73
+ }
74
+ if (self.client.isOpen) {
75
+ return self.client.sendCommand(args);
76
+ }
77
+ _common.TraceUtils.debug('RedisClientStore: Opening redis store client for sending commands');
78
+ return self.client.connect().then(() => {
79
+ // send load script commands once
80
+ return Promise.all([
81
+ superLoadIncrementScript.call(self),
82
+ superLoadGetScript.call(self)]).
83
+ then((results) => {
84
+ self.incrementScriptSha = results[0];
85
+ self.getScriptSha = results[1];
86
+ // send command
87
+ args[1] = self.incrementScriptSha;
88
+ return self.client.sendCommand(args);
89
+ });
90
+ });
91
+ }
92
+ }); /**
93
+ * @type {import('redis').RedisClientType}
94
+ */_defineProperty(this, "client", void 0);this.init(options);
95
+ }
96
+
97
+ }exports.RedisClientStore = RedisClientStore;
98
+ //# sourceMappingURL=RedisClientStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RedisClientStore.js","names":["_common","require","_rateLimitRedis","_interopRequireDefault","_redis","obj","__esModule","default","_defineProperty","key","value","_toPropertyKey","Object","defineProperty","enumerable","configurable","writable","arg","_toPrimitive","String","input","hint","prim","Symbol","toPrimitive","undefined","res","call","TypeError","Number","superLoadIncrementScript","RedisStore","prototype","loadIncrementScript","superLoadGetScript","loadGetScript","noLoadGetScript","noLoadIncrementScript","RedisClientStore","constructor","service","options","sendCommand","args","Array","from","arguments","self","TraceUtils","debug","connectOptions","getApplication","getConfiguration","getSourceAt","host","port","client","createClient","connect","then","catch","error","Promise","reject","finally","isOpen","disconnect","errDisconnect","all","results","incrementScriptSha","getScriptSha","init","exports"],"sources":["../src/RedisClientStore.js"],"sourcesContent":["import { TraceUtils } from '@themost/common';\nimport RedisStore from 'rate-limit-redis';\nimport { createClient } from 'redis';\n\nconst superLoadIncrementScript = RedisStore.prototype.loadIncrementScript;\nconst superLoadGetScript = RedisStore.prototype.loadGetScript;\n\nfunction noLoadGetScript() {\n //\n}\n\nfunction noLoadIncrementScript() {\n //\n}\n\nif (superLoadIncrementScript != noLoadIncrementScript) {\n RedisStore.prototype.loadIncrementScript = noLoadIncrementScript;\n}\n\nif (superLoadGetScript != noLoadGetScript) {\n RedisStore.prototype.loadGetScript = noLoadGetScript;\n}\n\nclass RedisClientStore extends RedisStore {\n\n /**\n * @type {import('redis').RedisClientType}\n */\n client;\n\n /**\n * \n * @param {import('@themost/common').ApplicationService} service\n * @param {{windowMs: number}} options\n */\n constructor(service, options) {\n super({\n /**\n * @param {...string} args\n * @returns {Promise<*>}\n */\n sendCommand: function () {\n const args = Array.from(arguments);\n const self = this;\n if (args[0] === 'SCRIPT') {\n TraceUtils.debug('RedisClientStore: Creating new client for sending script command');\n const connectOptions = service.getApplication().getConfiguration().getSourceAt('settings/redis/options') || {\n host: '127.0.0.1',\n port: 6379\n };\n const client = createClient(connectOptions);\n return client.connect().then(() => {\n return client.sendCommand(args);\n }).catch((error) => {\n TraceUtils.error(error);\n return Promise.reject(error);\n }).finally(() => {\n if (client.isOpen) {\n TraceUtils.debug('RedisClientStore: Closing client of sending script command');\n client.disconnect().catch((errDisconnect) => {\n TraceUtils.error(errDisconnect);\n });\n }\n });\n }\n if (self.client == null) {\n TraceUtils.debug('RedisClientStore: Creating store client for sending commands');\n const connectOptions = service.getApplication().getConfiguration().getSourceAt('settings/redis/options') || {\n host: '127.0.0.1',\n port: 6379\n };\n self.client = createClient(connectOptions);\n }\n if (self.client.isOpen) {\n return self.client.sendCommand(args);\n }\n TraceUtils.debug('RedisClientStore: Opening redis store client for sending commands');\n return self.client.connect().then(() => {\n // send load script commands once\n return Promise.all([\n superLoadIncrementScript.call(self),\n superLoadGetScript.call(self)\n ]).then((results) => {\n self.incrementScriptSha = results[0];\n self.getScriptSha = results[1];\n // send command \n args[1] = self.incrementScriptSha;\n return self.client.sendCommand(args);\n });\n });\n }\n });\n this.init(options)\n \n }\n\n}\n\nexport {\n RedisClientStore\n}"],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * @license
3
+ * Universis Project Version 1.0
4
+ * Copyright (c) 2018, Universis Project All rights reserved
5
+ *
6
+ * Use of this source code is governed by an LGPL 3.0 license that can be
7
+ * found in the LICENSE file at https://universis.io/license
8
+ */
9
+ import {ConfigurationBase, ConfigurationStrategy} from '@themost/common';
10
+
11
+ /**
12
+ * Declares a configuration element for managing scope-based permissions on server resources
13
+ */
14
+ declare interface ScopeAccessConfigurationElement {
15
+ /**
16
+ * Gets or sets an array of strings that holds an array of scopes e.g. students or students:read or students,teachers etc
17
+ */
18
+ scope: Array<string>,
19
+ /**
20
+ * Gets or sets a string which represents the regular expression that is going to used to validate endpoint
21
+ */
22
+ resource: string;
23
+ /**
24
+ * Gets or sets an array of strings which represents the access levels for the given scopes e.g. READ or READ,WRITE etc
25
+ */
26
+ access: Array<string>;
27
+ /**
28
+ * Gets or sets a string which represents a short description for this item
29
+ */
30
+ description?: string;
31
+ }
32
+
33
+ export declare class ScopeAccessConfiguration extends ConfigurationStrategy {
34
+
35
+ constructor(configuration: ConfigurationBase);
36
+
37
+ /**
38
+ * Gets an array of scope access configuration elements
39
+ */
40
+ public elements: Array<ScopeAccessConfigurationElement>;
41
+
42
+ /**
43
+ * Verifies the given request and returns a promise that resolves with a scope access configuration element
44
+ */
45
+ verify(req: Request): Promise<ScopeAccessConfigurationElement>;
46
+ }
47
+
48
+ export declare class DefaultScopeAccessConfiguration extends ScopeAccessConfiguration {
49
+
50
+ constructor(configuration: ConfigurationBase);
51
+ /**
52
+ * Gets an array of scope access configuration elements
53
+ */
54
+ public elements: Array<ScopeAccessConfigurationElement>;
55
+ /**
56
+ * Verifies the given request and returns a promise that resolves with a scope access configuration element
57
+ */
58
+ verify(req: Request): Promise<ScopeAccessConfigurationElement>;
59
+
60
+ }
@@ -0,0 +1,93 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.ScopeAccessConfiguration = exports.DefaultScopeAccessConfiguration = void 0;var _common = require("@themost/common");
2
+ var _path = _interopRequireDefault(require("path"));
3
+ var _url = _interopRequireDefault(require("url"));function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };}
4
+
5
+ const HTTP_METHOD_REGEXP = /^\b(POST|PUT|PATCH|DELETE)\b$/i;
6
+
7
+ class ScopeAccessConfiguration extends _common.ConfigurationStrategy {
8
+ /**
9
+ * @param {ConfigurationBase} configuration
10
+ */
11
+ constructor(configuration) {
12
+ super(configuration);
13
+ let elements = [];
14
+ // define property
15
+ Object.defineProperty(this, 'elements', {
16
+ get: () => {
17
+ return elements;
18
+ },
19
+ enumerable: true
20
+ });
21
+ }
22
+
23
+ /**
24
+ * @param {Request} req
25
+ * @returns Promise<ScopeAccessConfigurationElement>
26
+ */
27
+ verify(req) {
28
+ return new Promise((resolve, reject) => {
29
+ try {
30
+ // validate request context
31
+ _common.Args.notNull(req.context, 'Context');
32
+ // validate request context user
33
+ _common.Args.notNull(req.context.user, 'User');
34
+ if (req.context.user.authenticationScope && req.context.user.authenticationScope.length > 0) {
35
+ // get original url
36
+ let reqUrl = _url.default.parse(req.originalUrl).pathname;
37
+ // get user context scopes as array e.g, ['students', 'students:read']
38
+ let reqScopes = req.context.user.authenticationScope.split(',');
39
+ // get user access based on HTTP method e.g. GET -> read access
40
+ let reqAccess = HTTP_METHOD_REGEXP.test(req.method) ? 'write' : 'read';
41
+ let result = this.elements.find((x) => {
42
+ // filter element by access level
43
+ return x.access.indexOf(reqAccess) >= 0
44
+ // resource path
45
+ && new RegExp('^' + x.resource, 'i').test(reqUrl)
46
+ // and scopes
47
+ && x.scope.find((y) => {
48
+ // search user scopes (validate wildcard scope)
49
+ return y === '*' || reqScopes.indexOf(y) >= 0;
50
+ });
51
+ });
52
+ return resolve(result);
53
+ }
54
+ return resolve();
55
+ }
56
+ catch (err) {
57
+ return reject(err);
58
+ }
59
+
60
+ });
61
+ }
62
+
63
+ }
64
+
65
+ /**
66
+ * @class
67
+ */exports.ScopeAccessConfiguration = ScopeAccessConfiguration;
68
+ class DefaultScopeAccessConfiguration extends ScopeAccessConfiguration {
69
+ /**
70
+ * @param {ConfigurationBase} configuration
71
+ */
72
+ constructor(configuration) {
73
+ super(configuration);
74
+ let defaults = [];
75
+ // load scope access from configuration resource
76
+ try {
77
+ /**
78
+ * @type {Array<ScopeAccessConfigurationElement>}
79
+ */
80
+ defaults = require(_path.default.resolve(configuration.getConfigurationPath(), 'scope.access.json'));
81
+ }
82
+ catch (err) {
83
+ // if an error occurred other than module not found (there are no default access policies)
84
+ if (err.code !== 'MODULE_NOT_FOUND') {
85
+ // throw error
86
+ throw err;
87
+ }
88
+ // otherwise continue
89
+ }
90
+ this.elements.push.apply(this.elements, defaults);
91
+ }
92
+ }exports.DefaultScopeAccessConfiguration = DefaultScopeAccessConfiguration;
93
+ //# sourceMappingURL=ScopeAccessConfiguration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScopeAccessConfiguration.js","names":["_common","require","_path","_interopRequireDefault","_url","obj","__esModule","default","HTTP_METHOD_REGEXP","ScopeAccessConfiguration","ConfigurationStrategy","constructor","configuration","elements","Object","defineProperty","get","enumerable","verify","req","Promise","resolve","reject","Args","notNull","context","user","authenticationScope","length","reqUrl","url","parse","originalUrl","pathname","reqScopes","split","reqAccess","test","method","result","find","x","access","indexOf","RegExp","resource","scope","y","err","exports","DefaultScopeAccessConfiguration","defaults","path","getConfigurationPath","code","push","apply"],"sources":["../src/ScopeAccessConfiguration.js"],"sourcesContent":["import {ConfigurationStrategy, Args} from '@themost/common';\nimport path from 'path';\nimport url from 'url';\n\nconst HTTP_METHOD_REGEXP = /^\\b(POST|PUT|PATCH|DELETE)\\b$/i;\n\nclass ScopeAccessConfiguration extends ConfigurationStrategy {\n /**\n * @param {ConfigurationBase} configuration\n */\n constructor(configuration) {\n super(configuration);\n let elements = [];\n // define property\n Object.defineProperty(this, 'elements', {\n get: () => {\n return elements;\n },\n enumerable: true\n });\n }\n\n /**\n * @param {Request} req\n * @returns Promise<ScopeAccessConfigurationElement>\n */\n verify(req) {\n return new Promise((resolve, reject) => {\n try {\n // validate request context\n Args.notNull(req.context,'Context');\n // validate request context user\n Args.notNull(req.context.user,'User');\n if (req.context.user.authenticationScope && req.context.user.authenticationScope.length>0) {\n // get original url\n let reqUrl = url.parse(req.originalUrl).pathname;\n // get user context scopes as array e.g, ['students', 'students:read']\n let reqScopes = req.context.user.authenticationScope.split(',');\n // get user access based on HTTP method e.g. GET -> read access\n let reqAccess = HTTP_METHOD_REGEXP.test(req.method) ? 'write' : 'read';\n let result = this.elements.find(x => {\n // filter element by access level\n return x.access.indexOf(reqAccess)>=0\n // resource path\n && new RegExp( '^' + x.resource, 'i').test(reqUrl)\n // and scopes\n && x.scope.find(y => {\n // search user scopes (validate wildcard scope)\n return y === '*' || reqScopes.indexOf(y)>=0;\n });\n });\n return resolve(result);\n }\n return resolve();\n }\n catch(err) {\n return reject(err);\n }\n\n });\n }\n\n}\n\n/**\n * @class\n */\nclass DefaultScopeAccessConfiguration extends ScopeAccessConfiguration {\n /**\n * @param {ConfigurationBase} configuration\n */\n constructor(configuration) {\n super(configuration);\n let defaults = [];\n // load scope access from configuration resource\n try {\n /**\n * @type {Array<ScopeAccessConfigurationElement>}\n */\n defaults = require(path.resolve(configuration.getConfigurationPath(), 'scope.access.json'));\n }\n catch(err) {\n // if an error occurred other than module not found (there are no default access policies)\n if (err.code !== 'MODULE_NOT_FOUND') {\n // throw error\n throw err;\n }\n // otherwise continue\n }\n this.elements.push.apply(this.elements, defaults);\n }\n}\n\nexport {\n ScopeAccessConfiguration,\n DefaultScopeAccessConfiguration\n}\n"],"mappings":"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"}
@@ -49,9 +49,28 @@ class SpeedLimitService extends _common.ApplicationService {
49
49
  maxDelayMs: 20000 // 20 seconds
50
50
  }, profile, {
51
51
  keyGenerator: (req) => {
52
- return req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);
52
+ const ip = req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);
53
+ return `${path}:${ip}`;
53
54
  }
54
55
  });
56
+ if (typeof slowDownOptions.store === 'string') {
57
+ // load store
58
+ const store = slowDownOptions.store.split('#');
59
+ let StoreClass;
60
+ if (store.length === 2) {
61
+ const storeModule = require(store[0]);
62
+ if (Object.prototype.hasOwnProperty.call(storeModule, store[1])) {
63
+ StoreClass = storeModule[store[1]];
64
+ slowDownOptions.store = new StoreClass(this, slowDownOptions);
65
+ } else {
66
+ throw new Error(`${store} cannot be found or is inaccessible`);
67
+ }
68
+ } else {
69
+ StoreClass = require(store[0]);
70
+ // create store
71
+ slowDownOptions.store = new StoreClass(this, slowDownOptions);
72
+ }
73
+ }
55
74
  addRouter.use(path, (0, _expressSlowDown.default)(slowDownOptions));
56
75
  }
57
76
  });
@@ -1 +1 @@
1
- {"version":3,"file":"SpeedLimitService.js","names":["_common","require","_expressSlowDown","_interopRequireDefault","_express","_path","obj","__esModule","default","SpeedLimitService","ApplicationService","constructor","app","serviceRouter","subscribe","addRouter","express","Router","serviceConfiguration","getConfiguration","getSourceAt","profiles","paths","extends","configurationPath","getConfigurationPath","extendsPath","path","resolve","TraceUtils","log","pathsArray","profilesArray","Map","size","warn","forEach","value","profile","get","slowDownOptions","Object","assign","windowMs","delayAfter","delayMs","maxDelayMs","keyGenerator","req","headers","connection","remoteAddress","socket","use","slowDown","stack","length","unshift","apply","err","error","exports"],"sources":["../src/SpeedLimitService.js"],"sourcesContent":["import { ApplicationService, TraceUtils } from '@themost/common';\nimport slowDown from 'express-slow-down';\nimport express from 'express';\nimport path from 'path';\n\nexport class SpeedLimitService extends ApplicationService {\n constructor(app) {\n super(app);\n\n app.serviceRouter.subscribe(serviceRouter => {\n if (serviceRouter == null) {\n return;\n }\n try {\n const addRouter = express.Router();\n let serviceConfiguration = app.getConfiguration().getSourceAt('settings/universis/janitor/speedLimit') || {\n profiles: [],\n paths: []\n };\n if (serviceConfiguration.extends) {\n // get additional configuration\n const configurationPath = app.getConfiguration().getConfigurationPath();\n const extendsPath = path.resolve(configurationPath, serviceConfiguration.extends);\n TraceUtils.log(`@universis/janitor#SpeedLimitService will try to extend service configuration from ${extendsPath}`);\n serviceConfiguration = require(extendsPath);\n }\n const pathsArray = serviceConfiguration.paths || [];\n const profilesArray = serviceConfiguration.profiles || [];\n // create maps\n const paths = new Map(pathsArray);\n const profiles = new Map(profilesArray);\n if (paths.size === 0) {\n TraceUtils.warn('@universis/janitor#SpeedLimitService is being started but the collection of paths is empty.');\n }\n paths.forEach((value, path) => {\n let profile;\n // get profile\n if (value.profile) {\n profile = profiles.get(value.profile);\n } else {\n // or options defined inline\n profile = value\n }\n if (profile != null) {\n const slowDownOptions = Object.assign({\n windowMs: 5 * 60 * 1000, // 5 minutes\n delayAfter: 5, // 5 requests\n delayMs: 500, // 500 ms\n maxDelayMs: 20000 // 20 seconds\n }, profile, {\n keyGenerator: (req) => {\n return req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);\n }\n });\n addRouter.use(path, slowDown(slowDownOptions));\n }\n });\n if (addRouter.stack.length) {\n serviceRouter.stack.unshift.apply(serviceRouter.stack, addRouter.stack);\n }\n } catch (err) {\n TraceUtils.error('An error occurred while validating speed limit configuration.');\n TraceUtils.error(err);\n TraceUtils.warn('Speed limit service is inactive due to an error occured while loading configuration.')\n }\n });\n }\n\n}"],"mappings":"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"}
1
+ {"version":3,"file":"SpeedLimitService.js","names":["_common","require","_expressSlowDown","_interopRequireDefault","_express","_path","obj","__esModule","default","SpeedLimitService","ApplicationService","constructor","app","serviceRouter","subscribe","addRouter","express","Router","serviceConfiguration","getConfiguration","getSourceAt","profiles","paths","extends","configurationPath","getConfigurationPath","extendsPath","path","resolve","TraceUtils","log","pathsArray","profilesArray","Map","size","warn","forEach","value","profile","get","slowDownOptions","Object","assign","windowMs","delayAfter","delayMs","maxDelayMs","keyGenerator","req","ip","headers","connection","remoteAddress","socket","store","split","StoreClass","length","storeModule","prototype","hasOwnProperty","call","Error","use","slowDown","stack","unshift","apply","err","error","exports"],"sources":["../src/SpeedLimitService.js"],"sourcesContent":["import { ApplicationService, TraceUtils } from '@themost/common';\nimport slowDown from 'express-slow-down';\nimport express from 'express';\nimport path from 'path';\n\nexport class SpeedLimitService extends ApplicationService {\n constructor(app) {\n super(app);\n\n app.serviceRouter.subscribe(serviceRouter => {\n if (serviceRouter == null) {\n return;\n }\n try {\n const addRouter = express.Router();\n let serviceConfiguration = app.getConfiguration().getSourceAt('settings/universis/janitor/speedLimit') || {\n profiles: [],\n paths: []\n };\n if (serviceConfiguration.extends) {\n // get additional configuration\n const configurationPath = app.getConfiguration().getConfigurationPath();\n const extendsPath = path.resolve(configurationPath, serviceConfiguration.extends);\n TraceUtils.log(`@universis/janitor#SpeedLimitService will try to extend service configuration from ${extendsPath}`);\n serviceConfiguration = require(extendsPath);\n }\n const pathsArray = serviceConfiguration.paths || [];\n const profilesArray = serviceConfiguration.profiles || [];\n // create maps\n const paths = new Map(pathsArray);\n const profiles = new Map(profilesArray);\n if (paths.size === 0) {\n TraceUtils.warn('@universis/janitor#SpeedLimitService is being started but the collection of paths is empty.');\n }\n paths.forEach((value, path) => {\n let profile;\n // get profile\n if (value.profile) {\n profile = profiles.get(value.profile);\n } else {\n // or options defined inline\n profile = value\n }\n if (profile != null) {\n const slowDownOptions = Object.assign({\n windowMs: 5 * 60 * 1000, // 5 minutes\n delayAfter: 5, // 5 requests\n delayMs: 500, // 500 ms\n maxDelayMs: 20000 // 20 seconds\n }, profile, {\n keyGenerator: (req) => {\n const ip = req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);\n return `${path}:${ip}`;\n }\n });\n if (typeof slowDownOptions.store === 'string') {\n // load store\n const store = slowDownOptions.store.split('#');\n let StoreClass;\n if (store.length === 2) {\n const storeModule = require(store[0]);\n if (Object.prototype.hasOwnProperty.call(storeModule, store[1])) {\n StoreClass = storeModule[store[1]];\n slowDownOptions.store = new StoreClass(this, slowDownOptions);\n } else {\n throw new Error(`${store} cannot be found or is inaccessible`);\n }\n } else {\n StoreClass = require(store[0]);\n // create store\n slowDownOptions.store = new StoreClass(this, slowDownOptions);\n }\n }\n addRouter.use(path, slowDown(slowDownOptions));\n }\n });\n if (addRouter.stack.length) {\n serviceRouter.stack.unshift.apply(serviceRouter.stack, addRouter.stack);\n }\n } catch (err) {\n TraceUtils.error('An error occurred while validating speed limit configuration.');\n TraceUtils.error(err);\n TraceUtils.warn('Speed limit service is inactive due to an error occured while loading configuration.')\n }\n });\n }\n\n}"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,4 @@
1
1
  export * from './RateLimitService';
2
- export * from './SpeedLimitService';
2
+ export * from './SpeedLimitService';
3
+ export * from './RedisClientStore';
4
+ export * from './ScopeAccessConfiguration';
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", { value: true });var _RateLimitService = require("./RateLimitService");Object.keys(_RateLimitService).forEach(function (key) {if (key === "default" || key === "__esModule") return;if (key in exports && exports[key] === _RateLimitService[key]) return;Object.defineProperty(exports, key, { enumerable: true, get: function () {return _RateLimitService[key];} });});
1
+ "use strict";Object.defineProperty(exports, "__esModule", { value: true });require("./polyfills");
2
+ var _RateLimitService = require("./RateLimitService");Object.keys(_RateLimitService).forEach(function (key) {if (key === "default" || key === "__esModule") return;if (key in exports && exports[key] === _RateLimitService[key]) return;Object.defineProperty(exports, key, { enumerable: true, get: function () {return _RateLimitService[key];} });});
2
3
  var _SpeedLimitService = require("./SpeedLimitService");Object.keys(_SpeedLimitService).forEach(function (key) {if (key === "default" || key === "__esModule") return;if (key in exports && exports[key] === _SpeedLimitService[key]) return;Object.defineProperty(exports, key, { enumerable: true, get: function () {return _SpeedLimitService[key];} });});
4
+ var _RedisClientStore = require("./RedisClientStore");Object.keys(_RedisClientStore).forEach(function (key) {if (key === "default" || key === "__esModule") return;if (key in exports && exports[key] === _RedisClientStore[key]) return;Object.defineProperty(exports, key, { enumerable: true, get: function () {return _RedisClientStore[key];} });});
5
+ var _ScopeAccessConfiguration = require("./ScopeAccessConfiguration");Object.keys(_ScopeAccessConfiguration).forEach(function (key) {if (key === "default" || key === "__esModule") return;if (key in exports && exports[key] === _ScopeAccessConfiguration[key]) return;Object.defineProperty(exports, key, { enumerable: true, get: function () {return _ScopeAccessConfiguration[key];} });});
3
6
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["_RateLimitService","require","Object","keys","forEach","key","exports","defineProperty","enumerable","get","_SpeedLimitService"],"sources":["../src/index.js"],"sourcesContent":["export * from './RateLimitService';\nexport * from './SpeedLimitService';"],"mappings":"2EAAA,IAAAA,iBAAA,GAAAC,OAAA,uBAAAC,MAAA,CAAAC,IAAA,CAAAH,iBAAA,EAAAI,OAAA,WAAAC,GAAA,OAAAA,GAAA,kBAAAA,GAAA,8BAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAL,iBAAA,CAAAK,GAAA,UAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA,IAAAG,UAAA,QAAAC,GAAA,WAAAA,CAAA,UAAAT,iBAAA,CAAAK,GAAA;AACA,IAAAK,kBAAA,GAAAT,OAAA,wBAAAC,MAAA,CAAAC,IAAA,CAAAO,kBAAA,EAAAN,OAAA,WAAAC,GAAA,OAAAA,GAAA,kBAAAA,GAAA,8BAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAK,kBAAA,CAAAL,GAAA,UAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA,IAAAG,UAAA,QAAAC,GAAA,WAAAA,CAAA,UAAAC,kBAAA,CAAAL,GAAA"}
1
+ {"version":3,"file":"index.js","names":["require","_RateLimitService","Object","keys","forEach","key","exports","defineProperty","enumerable","get","_SpeedLimitService","_RedisClientStore","_ScopeAccessConfiguration"],"sources":["../src/index.js"],"sourcesContent":["import './polyfills';\nexport * from './RateLimitService';\nexport * from './SpeedLimitService';\nexport * from './RedisClientStore';\nexport * from './ScopeAccessConfiguration';"],"mappings":"2EAAAA,OAAA;AACA,IAAAC,iBAAA,GAAAD,OAAA,uBAAAE,MAAA,CAAAC,IAAA,CAAAF,iBAAA,EAAAG,OAAA,WAAAC,GAAA,OAAAA,GAAA,kBAAAA,GAAA,8BAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAJ,iBAAA,CAAAI,GAAA,UAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA,IAAAG,UAAA,QAAAC,GAAA,WAAAA,CAAA,UAAAR,iBAAA,CAAAI,GAAA;AACA,IAAAK,kBAAA,GAAAV,OAAA,wBAAAE,MAAA,CAAAC,IAAA,CAAAO,kBAAA,EAAAN,OAAA,WAAAC,GAAA,OAAAA,GAAA,kBAAAA,GAAA,8BAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAK,kBAAA,CAAAL,GAAA,UAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA,IAAAG,UAAA,QAAAC,GAAA,WAAAA,CAAA,UAAAC,kBAAA,CAAAL,GAAA;AACA,IAAAM,iBAAA,GAAAX,OAAA,uBAAAE,MAAA,CAAAC,IAAA,CAAAQ,iBAAA,EAAAP,OAAA,WAAAC,GAAA,OAAAA,GAAA,kBAAAA,GAAA,8BAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAM,iBAAA,CAAAN,GAAA,UAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA,IAAAG,UAAA,QAAAC,GAAA,WAAAA,CAAA,UAAAE,iBAAA,CAAAN,GAAA;AACA,IAAAO,yBAAA,GAAAZ,OAAA,+BAAAE,MAAA,CAAAC,IAAA,CAAAS,yBAAA,EAAAR,OAAA,WAAAC,GAAA,OAAAA,GAAA,kBAAAA,GAAA,8BAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAO,yBAAA,CAAAP,GAAA,UAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA,IAAAG,UAAA,QAAAC,GAAA,WAAAA,CAAA,UAAAG,yBAAA,CAAAP,GAAA"}
@@ -0,0 +1,11 @@
1
+ "use strict";if (!String.prototype.replaceAll) {
2
+ String.prototype.replaceAll = function (str, newStr) {
3
+ // If a regex pattern
4
+ if (Object.prototype.toString.call(str).toLowerCase() === '[object regexp]') {
5
+ return this.replace(str, newStr);
6
+ }
7
+ // If a string
8
+ return this.replace(new RegExp(str, 'g'), newStr);
9
+ };
10
+ }
11
+ //# sourceMappingURL=polyfills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polyfills.js","names":["String","prototype","replaceAll","str","newStr","Object","toString","call","toLowerCase","replace","RegExp"],"sources":["../src/polyfills.js"],"sourcesContent":["if (!String.prototype.replaceAll) {\n\tString.prototype.replaceAll = function(str, newStr){\n\t\t// If a regex pattern\n\t\tif (Object.prototype.toString.call(str).toLowerCase() === '[object regexp]') {\n\t\t\treturn this.replace(str, newStr);\n\t\t}\n\t\t// If a string\n\t\treturn this.replace(new RegExp(str, 'g'), newStr);\n\t};\n}"],"mappings":"aAAA,IAAI,CAACA,MAAM,CAACC,SAAS,CAACC,UAAU,EAAE;EACjCF,MAAM,CAACC,SAAS,CAACC,UAAU,GAAG,UAASC,GAAG,EAAEC,MAAM,EAAC;IAClD;IACA,IAAIC,MAAM,CAACJ,SAAS,CAACK,QAAQ,CAACC,IAAI,CAACJ,GAAG,CAAC,CAACK,WAAW,EAAE,KAAK,iBAAiB,EAAE;MAC5E,OAAO,IAAI,CAACC,OAAO,CAACN,GAAG,EAAEC,MAAM,CAAC;IACjC;IACA;IACA,OAAO,IAAI,CAACK,OAAO,CAAC,IAAIC,MAAM,CAACP,GAAG,EAAE,GAAG,CAAC,EAAEC,MAAM,CAAC;EAClD,CAAC;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@universis/janitor",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Universis api plugin for rate limiting requests",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -30,7 +30,9 @@
30
30
  },
31
31
  "dependencies": {
32
32
  "express-rate-limit": "^7.1.1",
33
- "express-slow-down": "^1.6.0"
33
+ "express-slow-down": "^1.6.0",
34
+ "rate-limit-redis": "^4.1.2",
35
+ "redis": "^4.6.10"
34
36
  },
35
37
  "peerDependencies": {
36
38
  "@themost/common": "^2",
Binary file
@@ -0,0 +1,25 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3
+ <!-- Created with Vectornator (http://vectornator.io/) -->
4
+ <svg height="100%" stroke-miterlimit="10" style="fill-rule:nonzero;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;" version="1.1" viewBox="0 0 1024 1024" width="100%" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
5
+ <defs/>
6
+ <g id="Untitled">
7
+ <g opacity="1">
8
+ <g opacity="1">
9
+ <path d="M133.315 232.814L350.631 231.553L500.788 230.681" fill="none" opacity="1" stroke="#c73850" stroke-linecap="butt" stroke-linejoin="round" stroke-width="48"/>
10
+ <path d="M790.685 232.814L849.823 231.553L890.685 230.681" fill="none" opacity="1" stroke="#c73850" stroke-linecap="butt" stroke-linejoin="round" stroke-width="48"/>
11
+ <path d="M558.158 231.747C558.158 182.042 598.452 141.747 648.158 141.747C697.864 141.747 738.158 182.042 738.158 231.747C738.158 281.453 697.864 321.747 648.158 321.747C598.452 321.747 558.158 281.453 558.158 231.747Z" fill="none" opacity="1" stroke="#c73850" stroke-linecap="butt" stroke-linejoin="round" stroke-width="48"/>
12
+ </g>
13
+ <g opacity="1">
14
+ <path d="M890.685 516.481L673.369 517.742L523.212 518.614" fill="none" opacity="1" stroke="#c73850" stroke-linecap="butt" stroke-linejoin="round" stroke-width="48"/>
15
+ <path d="M233.315 516.481L174.177 517.742L133.315 518.614" fill="none" opacity="1" stroke="#c73850" stroke-linecap="butt" stroke-linejoin="round" stroke-width="48"/>
16
+ <path d="M465.842 517.547C465.842 567.253 425.548 607.547 375.842 607.547C326.136 607.547 285.842 567.253 285.842 517.547C285.842 467.842 326.136 427.547 375.842 427.547C425.548 427.547 465.842 467.842 465.842 517.547Z" fill="none" opacity="1" stroke="#c73850" stroke-linecap="butt" stroke-linejoin="round" stroke-width="48"/>
17
+ </g>
18
+ <g opacity="1">
19
+ <path d="M133.315 793.319L350.631 792.058L500.788 791.186" fill="none" opacity="1" stroke="#c73850" stroke-linecap="butt" stroke-linejoin="round" stroke-width="48"/>
20
+ <path d="M790.685 793.319L849.823 792.058L890.685 791.186" fill="none" opacity="1" stroke="#c73850" stroke-linecap="butt" stroke-linejoin="round" stroke-width="48"/>
21
+ <path d="M558.158 792.253C558.158 742.547 598.452 702.253 648.158 702.253C697.864 702.253 738.158 742.547 738.158 792.253C738.158 841.958 697.864 882.253 648.158 882.253C598.452 882.253 558.158 841.958 558.158 792.253Z" fill="none" opacity="1" stroke="#c73850" stroke-linecap="butt" stroke-linejoin="round" stroke-width="48"/>
22
+ </g>
23
+ </g>
24
+ </g>
25
+ </svg>
Binary file
Binary file
@@ -50,9 +50,28 @@ export class RateLimitService extends ApplicationService {
50
50
  legacyHeaders: true // send headers
51
51
  }, profile, {
52
52
  keyGenerator: (req) => {
53
- return req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);
53
+ const ip = req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);
54
+ return `${path}:${ip}`;
54
55
  }
55
56
  });
57
+ if (typeof rateLimitOptions.store === 'string') {
58
+ // load store
59
+ const store = rateLimitOptions.store.split('#');
60
+ let StoreClass;
61
+ if (store.length === 2) {
62
+ const storeModule = require(store[0]);
63
+ if (Object.prototype.hasOwnProperty.call(storeModule, store[1])) {
64
+ StoreClass = storeModule[store[1]];
65
+ rateLimitOptions.store = new StoreClass(this, rateLimitOptions);
66
+ } else {
67
+ throw new Error(`${store} cannot be found or is inaccessible`);
68
+ }
69
+ } else {
70
+ StoreClass = require(store[0]);
71
+ // create store
72
+ rateLimitOptions.store = new StoreClass(this, rateLimitOptions);
73
+ }
74
+ }
56
75
  addRouter.use(path, rateLimit(rateLimitOptions));
57
76
  }
58
77
  });
@@ -0,0 +1,5 @@
1
+ import { ApplicationService } from '@themost/common';
2
+ import RedisStore from 'rate-limit-redis';
3
+ export declare class RedisClientStore extends RedisStore {
4
+ constructor(app: ApplicationService);
5
+ }
@@ -0,0 +1,101 @@
1
+ import { TraceUtils } from '@themost/common';
2
+ import RedisStore from 'rate-limit-redis';
3
+ import { createClient } from 'redis';
4
+
5
+ const superLoadIncrementScript = RedisStore.prototype.loadIncrementScript;
6
+ const superLoadGetScript = RedisStore.prototype.loadGetScript;
7
+
8
+ function noLoadGetScript() {
9
+ //
10
+ }
11
+
12
+ function noLoadIncrementScript() {
13
+ //
14
+ }
15
+
16
+ if (superLoadIncrementScript != noLoadIncrementScript) {
17
+ RedisStore.prototype.loadIncrementScript = noLoadIncrementScript;
18
+ }
19
+
20
+ if (superLoadGetScript != noLoadGetScript) {
21
+ RedisStore.prototype.loadGetScript = noLoadGetScript;
22
+ }
23
+
24
+ class RedisClientStore extends RedisStore {
25
+
26
+ /**
27
+ * @type {import('redis').RedisClientType}
28
+ */
29
+ client;
30
+
31
+ /**
32
+ *
33
+ * @param {import('@themost/common').ApplicationService} service
34
+ * @param {{windowMs: number}} options
35
+ */
36
+ constructor(service, options) {
37
+ super({
38
+ /**
39
+ * @param {...string} args
40
+ * @returns {Promise<*>}
41
+ */
42
+ sendCommand: function () {
43
+ const args = Array.from(arguments);
44
+ const self = this;
45
+ if (args[0] === 'SCRIPT') {
46
+ TraceUtils.debug('RedisClientStore: Creating new client for sending script command');
47
+ const connectOptions = service.getApplication().getConfiguration().getSourceAt('settings/redis/options') || {
48
+ host: '127.0.0.1',
49
+ port: 6379
50
+ };
51
+ const client = createClient(connectOptions);
52
+ return client.connect().then(() => {
53
+ return client.sendCommand(args);
54
+ }).catch((error) => {
55
+ TraceUtils.error(error);
56
+ return Promise.reject(error);
57
+ }).finally(() => {
58
+ if (client.isOpen) {
59
+ TraceUtils.debug('RedisClientStore: Closing client of sending script command');
60
+ client.disconnect().catch((errDisconnect) => {
61
+ TraceUtils.error(errDisconnect);
62
+ });
63
+ }
64
+ });
65
+ }
66
+ if (self.client == null) {
67
+ TraceUtils.debug('RedisClientStore: Creating store client for sending commands');
68
+ const connectOptions = service.getApplication().getConfiguration().getSourceAt('settings/redis/options') || {
69
+ host: '127.0.0.1',
70
+ port: 6379
71
+ };
72
+ self.client = createClient(connectOptions);
73
+ }
74
+ if (self.client.isOpen) {
75
+ return self.client.sendCommand(args);
76
+ }
77
+ TraceUtils.debug('RedisClientStore: Opening redis store client for sending commands');
78
+ return self.client.connect().then(() => {
79
+ // send load script commands once
80
+ return Promise.all([
81
+ superLoadIncrementScript.call(self),
82
+ superLoadGetScript.call(self)
83
+ ]).then((results) => {
84
+ self.incrementScriptSha = results[0];
85
+ self.getScriptSha = results[1];
86
+ // send command
87
+ args[1] = self.incrementScriptSha;
88
+ return self.client.sendCommand(args);
89
+ });
90
+ });
91
+ }
92
+ });
93
+ this.init(options)
94
+
95
+ }
96
+
97
+ }
98
+
99
+ export {
100
+ RedisClientStore
101
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * @license
3
+ * Universis Project Version 1.0
4
+ * Copyright (c) 2018, Universis Project All rights reserved
5
+ *
6
+ * Use of this source code is governed by an LGPL 3.0 license that can be
7
+ * found in the LICENSE file at https://universis.io/license
8
+ */
9
+ import {ConfigurationBase, ConfigurationStrategy} from '@themost/common';
10
+
11
+ /**
12
+ * Declares a configuration element for managing scope-based permissions on server resources
13
+ */
14
+ declare interface ScopeAccessConfigurationElement {
15
+ /**
16
+ * Gets or sets an array of strings that holds an array of scopes e.g. students or students:read or students,teachers etc
17
+ */
18
+ scope: Array<string>,
19
+ /**
20
+ * Gets or sets a string which represents the regular expression that is going to used to validate endpoint
21
+ */
22
+ resource: string;
23
+ /**
24
+ * Gets or sets an array of strings which represents the access levels for the given scopes e.g. READ or READ,WRITE etc
25
+ */
26
+ access: Array<string>;
27
+ /**
28
+ * Gets or sets a string which represents a short description for this item
29
+ */
30
+ description?: string;
31
+ }
32
+
33
+ export declare class ScopeAccessConfiguration extends ConfigurationStrategy {
34
+
35
+ constructor(configuration: ConfigurationBase);
36
+
37
+ /**
38
+ * Gets an array of scope access configuration elements
39
+ */
40
+ public elements: Array<ScopeAccessConfigurationElement>;
41
+
42
+ /**
43
+ * Verifies the given request and returns a promise that resolves with a scope access configuration element
44
+ */
45
+ verify(req: Request): Promise<ScopeAccessConfigurationElement>;
46
+ }
47
+
48
+ export declare class DefaultScopeAccessConfiguration extends ScopeAccessConfiguration {
49
+
50
+ constructor(configuration: ConfigurationBase);
51
+ /**
52
+ * Gets an array of scope access configuration elements
53
+ */
54
+ public elements: Array<ScopeAccessConfigurationElement>;
55
+ /**
56
+ * Verifies the given request and returns a promise that resolves with a scope access configuration element
57
+ */
58
+ verify(req: Request): Promise<ScopeAccessConfigurationElement>;
59
+
60
+ }
@@ -0,0 +1,97 @@
1
+ import {ConfigurationStrategy, Args} from '@themost/common';
2
+ import path from 'path';
3
+ import url from 'url';
4
+
5
+ const HTTP_METHOD_REGEXP = /^\b(POST|PUT|PATCH|DELETE)\b$/i;
6
+
7
+ class ScopeAccessConfiguration extends ConfigurationStrategy {
8
+ /**
9
+ * @param {ConfigurationBase} configuration
10
+ */
11
+ constructor(configuration) {
12
+ super(configuration);
13
+ let elements = [];
14
+ // define property
15
+ Object.defineProperty(this, 'elements', {
16
+ get: () => {
17
+ return elements;
18
+ },
19
+ enumerable: true
20
+ });
21
+ }
22
+
23
+ /**
24
+ * @param {Request} req
25
+ * @returns Promise<ScopeAccessConfigurationElement>
26
+ */
27
+ verify(req) {
28
+ return new Promise((resolve, reject) => {
29
+ try {
30
+ // validate request context
31
+ Args.notNull(req.context,'Context');
32
+ // validate request context user
33
+ Args.notNull(req.context.user,'User');
34
+ if (req.context.user.authenticationScope && req.context.user.authenticationScope.length>0) {
35
+ // get original url
36
+ let reqUrl = url.parse(req.originalUrl).pathname;
37
+ // get user context scopes as array e.g, ['students', 'students:read']
38
+ let reqScopes = req.context.user.authenticationScope.split(',');
39
+ // get user access based on HTTP method e.g. GET -> read access
40
+ let reqAccess = HTTP_METHOD_REGEXP.test(req.method) ? 'write' : 'read';
41
+ let result = this.elements.find(x => {
42
+ // filter element by access level
43
+ return x.access.indexOf(reqAccess)>=0
44
+ // resource path
45
+ && new RegExp( '^' + x.resource, 'i').test(reqUrl)
46
+ // and scopes
47
+ && x.scope.find(y => {
48
+ // search user scopes (validate wildcard scope)
49
+ return y === '*' || reqScopes.indexOf(y)>=0;
50
+ });
51
+ });
52
+ return resolve(result);
53
+ }
54
+ return resolve();
55
+ }
56
+ catch(err) {
57
+ return reject(err);
58
+ }
59
+
60
+ });
61
+ }
62
+
63
+ }
64
+
65
+ /**
66
+ * @class
67
+ */
68
+ class DefaultScopeAccessConfiguration extends ScopeAccessConfiguration {
69
+ /**
70
+ * @param {ConfigurationBase} configuration
71
+ */
72
+ constructor(configuration) {
73
+ super(configuration);
74
+ let defaults = [];
75
+ // load scope access from configuration resource
76
+ try {
77
+ /**
78
+ * @type {Array<ScopeAccessConfigurationElement>}
79
+ */
80
+ defaults = require(path.resolve(configuration.getConfigurationPath(), 'scope.access.json'));
81
+ }
82
+ catch(err) {
83
+ // if an error occurred other than module not found (there are no default access policies)
84
+ if (err.code !== 'MODULE_NOT_FOUND') {
85
+ // throw error
86
+ throw err;
87
+ }
88
+ // otherwise continue
89
+ }
90
+ this.elements.push.apply(this.elements, defaults);
91
+ }
92
+ }
93
+
94
+ export {
95
+ ScopeAccessConfiguration,
96
+ DefaultScopeAccessConfiguration
97
+ }
@@ -49,9 +49,28 @@ export class SpeedLimitService extends ApplicationService {
49
49
  maxDelayMs: 20000 // 20 seconds
50
50
  }, profile, {
51
51
  keyGenerator: (req) => {
52
- return req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);
52
+ const ip = req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);
53
+ return `${path}:${ip}`;
53
54
  }
54
55
  });
56
+ if (typeof slowDownOptions.store === 'string') {
57
+ // load store
58
+ const store = slowDownOptions.store.split('#');
59
+ let StoreClass;
60
+ if (store.length === 2) {
61
+ const storeModule = require(store[0]);
62
+ if (Object.prototype.hasOwnProperty.call(storeModule, store[1])) {
63
+ StoreClass = storeModule[store[1]];
64
+ slowDownOptions.store = new StoreClass(this, slowDownOptions);
65
+ } else {
66
+ throw new Error(`${store} cannot be found or is inaccessible`);
67
+ }
68
+ } else {
69
+ StoreClass = require(store[0]);
70
+ // create store
71
+ slowDownOptions.store = new StoreClass(this, slowDownOptions);
72
+ }
73
+ }
55
74
  addRouter.use(path, slowDown(slowDownOptions));
56
75
  }
57
76
  });
package/src/index.d.ts CHANGED
@@ -1,2 +1,4 @@
1
1
  export * from './RateLimitService';
2
- export * from './SpeedLimitService';
2
+ export * from './SpeedLimitService';
3
+ export * from './RedisClientStore';
4
+ export * from './ScopeAccessConfiguration';
package/src/index.js CHANGED
@@ -1,2 +1,5 @@
1
+ import './polyfills';
1
2
  export * from './RateLimitService';
2
- export * from './SpeedLimitService';
3
+ export * from './SpeedLimitService';
4
+ export * from './RedisClientStore';
5
+ export * from './ScopeAccessConfiguration';
@@ -0,0 +1,10 @@
1
+ if (!String.prototype.replaceAll) {
2
+ String.prototype.replaceAll = function(str, newStr){
3
+ // If a regex pattern
4
+ if (Object.prototype.toString.call(str).toLowerCase() === '[object regexp]') {
5
+ return this.replace(str, newStr);
6
+ }
7
+ // If a string
8
+ return this.replace(new RegExp(str, 'g'), newStr);
9
+ };
10
+ }
@@ -1,5 +0,0 @@
1
- import { DataObject } from '@themost/data';
2
-
3
- export declare class TestAction extends DataObject {
4
-
5
- }
@@ -1,5 +0,0 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", { value: true });exports.TestAction = void 0;var _data = require("@themost/data");var _dec, _class;let
2
-
3
-
4
- TestAction = (_dec = _data.EdmMapping.entityType(), _dec(_class = class TestAction extends _data.DataObject {}) || _class);exports.TestAction = TestAction;
5
- //# sourceMappingURL=TestAction.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"TestAction.js","names":["_data","require","_dec","_class","TestAction","EdmMapping","entityType","DataObject","exports"],"sources":["../../src/models/TestAction.js"],"sourcesContent":["import { DataObject, EdmMapping } from \"@themost/data\";\n\n@EdmMapping.entityType()\nclass TestAction extends DataObject {\n\n}\n\nexport {\n TestAction\n}"],"mappings":"uGAAA,IAAAA,KAAA,GAAAC,OAAA,kBAAuD,IAAAC,IAAA,EAAAC,MAAA;;;AAGjDC,UAAU,IAAAF,IAAA,GADfG,gBAAU,CAACC,UAAU,EAAE,EAAAJ,IAAA,CAAAC,MAAA,GAAxB,MACMC,UAAU,SAASG,gBAAU,CAAC,EAEnC,KAAAJ,MAAA,EAAAK,OAAA,CAAAJ,UAAA,GAAAA,UAAA"}
Binary file
Binary file
@@ -1,13 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
- <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
3
- <!-- Created with Vectornator (http://vectornator.io/) -->
4
- <svg height="1451.5199999999998mm" stroke-miterlimit="10" style="fill-rule:nonzero;clip-rule:evenodd;stroke-linecap:round;stroke-linejoin:round;" version="1.1" viewBox="0 0 4115.06 4115.06" width="1451.5199999999998mm" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
5
- <defs/>
6
- <g id="Layer-1">
7
- <path d="M723.486 744.868L1942.84 744.767L3392.53 744.647" fill="none" opacity="1" stroke="#23b7cc" stroke-linecap="round" stroke-linejoin="round" stroke-width="283.5"/>
8
- <path d="M735.141 1225.03L1408.88 1968.33L1629.62 2205.76L2290.13 2916.22" fill="none" opacity="1" stroke="#23b7cc" stroke-linecap="round" stroke-linejoin="round" stroke-width="283.5"/>
9
- <path d="M2381.55 2272.88L2826.88 1797.13L3362.09 1218.8" fill="none" opacity="1" stroke="#23b7cc" stroke-linecap="round" stroke-linejoin="round" stroke-width="283.5"/>
10
- <path d="M2806.2 3518.14L2806.83 3058.9L2806.03 2876.36L2804.41 2504.02" fill="none" opacity="1" stroke="#23b7cc" stroke-linecap="round" stroke-linejoin="round" stroke-width="283.5"/>
11
- <path d="M817.89 2946.06L1633.75 2942.44" fill="none" opacity="1" stroke="#23b7cc" stroke-linecap="round" stroke-linejoin="round" stroke-width="283.5"/>
12
- </g>
13
- </svg>
@@ -1,5 +0,0 @@
1
- import { DataObject } from '@themost/data';
2
-
3
- export declare class TestAction extends DataObject {
4
-
5
- }
@@ -1,10 +0,0 @@
1
- import { DataObject, EdmMapping } from "@themost/data";
2
-
3
- @EdmMapping.entityType()
4
- class TestAction extends DataObject {
5
-
6
- }
7
-
8
- export {
9
- TestAction
10
- }