@universis/janitor 1.4.1 → 1.6.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.
@@ -0,0 +1,92 @@
1
+ import { ApplicationService, HttpForbiddenError, TraceUtils } from '@themost/common';
2
+ import express from 'express';
3
+ import jwt from 'jsonwebtoken';
4
+
5
+ class HttpRemoteAddrForbiddenError extends HttpForbiddenError {
6
+ constructor() {
7
+ super('Access is denied due to remote address conflict. The client network has been changed or cannot be determined.');
8
+ this.statusCode = 403.6;
9
+ }
10
+
11
+ }
12
+
13
+ class RemoteAddressValidator extends ApplicationService {
14
+ constructor(app) {
15
+ super(app);
16
+
17
+ // get proxy address forwarding option
18
+ this.proxyAddressForwarding = app.getConfiguration().getSourceAt('settings/universis/api/proxyAddressForwarding');
19
+ if (typeof proxyAddressForwarding !== 'boolean') {
20
+ this.proxyAddressForwarding = false;
21
+ }
22
+ // get token claim name
23
+ this.claim = app.getConfiguration().getSourceAt('settings/universis/janitor/remoteAddress/claim') || 'remoteAddress';
24
+
25
+ app.serviceRouter.subscribe((serviceRouter) => {
26
+ if (serviceRouter == null) {
27
+ return;
28
+ }
29
+ const addRouter = express.Router();
30
+ addRouter.use((req, res, next) => {
31
+ void this.validateRemoteAddress(req).then((value) => {
32
+ if (value === false) {
33
+ return next(new HttpRemoteAddrForbiddenError());
34
+ }
35
+ return next();
36
+ }).catch((err) => {
37
+ return next(err);
38
+ });
39
+ });
40
+ // insert router at the beginning of serviceRouter.stack
41
+ serviceRouter.stack.unshift.apply(serviceRouter.stack, addRouter.stack);
42
+ });
43
+ }
44
+
45
+ /**
46
+ * Gets remote address from request
47
+ * @param {import('express').Request} req
48
+ * @returns
49
+ */
50
+ getRemoteAddress(req) {
51
+ let remoteAddress;
52
+ if (this.proxyAddressForwarding) {
53
+ // get proxy headers or remote address
54
+ remoteAddress = req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || (req.connection ? req.connection.remoteAddress : req.socket.remoteAddress);
55
+ } else {
56
+ remoteAddress = req.connection ? req.connection.remoteAddress : req.socket.remoteAddress;
57
+ }
58
+ return remoteAddress;
59
+ }
60
+
61
+ /**
62
+ * Validates token remote address with request remote address
63
+ * @param {import('express').Request} req
64
+ * @returns {Promise<boolean>}
65
+ */
66
+ async validateRemoteAddress(req) {
67
+ const authenticationToken = req.context?.user?.authenticationToken;
68
+ if (authenticationToken != null) {
69
+ const access_token = jwt.decode(authenticationToken);
70
+ const remoteAddress = access_token[this.claim];
71
+ if (remoteAddress == null) {
72
+ TraceUtils.warn(`Remote address validation failed. Expected a valid remote address claimed by using "${this.claim}" attribute but got none.`);
73
+ return false;
74
+ }
75
+ // get context remote address
76
+ const requestRemoteAddress = this.getRemoteAddress(req);
77
+ if (remoteAddress !== requestRemoteAddress) {
78
+ TraceUtils.warn(`Remote address validation failed. Expected remote address is ${remoteAddress || 'Uknown'} but request remote address is ${requestRemoteAddress}`);
79
+ return false;
80
+ }
81
+ return true;
82
+ }
83
+ TraceUtils.warn('Remote address validation cannot be completed because authentication token is not available.');
84
+ return false;
85
+ }
86
+
87
+ }
88
+
89
+ export {
90
+ HttpRemoteAddrForbiddenError,
91
+ RemoteAddressValidator
92
+ }
package/src/index.d.ts CHANGED
@@ -3,3 +3,5 @@ export * from './SpeedLimitService';
3
3
  export * from './RedisClientStore';
4
4
  export * from './ScopeAccessConfiguration';
5
5
  export * from './validateScope';
6
+ export * from './OAuth2ClientService';
7
+ export * from './RemoteAddressValidator';
package/src/index.js CHANGED
@@ -4,3 +4,5 @@ export * from './SpeedLimitService';
4
4
  export * from './RedisClientStore';
5
5
  export * from './ScopeAccessConfiguration';
6
6
  export * from './validateScope';
7
+ export * from './OAuth2ClientService';
8
+ export * from './RemoteAddressValidator';