-
Notifications
You must be signed in to change notification settings - Fork 0
/
proxy.ts
174 lines (149 loc) · 4.86 KB
/
proxy.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import { ClientRequest, createServer } from "http";
import { createProxyServer } from "http-proxy";
import dotenv from "dotenv";
import { hostname } from "os";
import { readFileSync } from "fs";
// ### Environment Variables ###
dotenv.config();
const LISTEN_HOST =
process.env.LISTEN_HOST!.toLowerCase() === "hostname"
? hostname()
: process.env.LISTEN_HOST!;
const LISTEN_PORT = parseInt(process.env.LISTEN_PORT!);
const TARGET_HOST = process.env.TARGET_HOST!;
const TARGET_PORT = parseInt(process.env.TARGET_PORT!);
// Secure iff env var is NOT in list below
const SECURE = !["undefined", "false"].includes(
("" + process.env.SECURE).toLowerCase()
);
const KEY =
SECURE && process.env.SSL_KEY_PATH
? readFileSync(process.env.SSL_KEY_PATH, "utf-8")
: undefined;
const CERT =
SECURE && process.env.SSL_CERT_PATH
? readFileSync(process.env.SSL_CERT_PATH, "utf-8")
: undefined;
const CA =
SECURE && process.env.SSL_CA_PATH
? readFileSync(process.env.SSL_CA_PATH, "utf-8")
: undefined;
const PT_AUTHORIZATION = process.env.PT_AUTHORIZATION;
const EXPECTED_GENE42_SECRET = process.env.PT_SECRET;
if (PT_AUTHORIZATION === undefined || PT_AUTHORIZATION === "")
throw Error("PT_AUTHORIZATION not set in env!");
if (EXPECTED_GENE42_SECRET === undefined || EXPECTED_GENE42_SECRET === "")
throw Error("PT_SECRET not set in env!");
var proxy = createProxyServer({
target: `http${SECURE ? "s" : ""}://${TARGET_HOST}:${TARGET_PORT}`,
ssl: SECURE
? {
key: KEY,
cert: CERT,
}
: undefined,
secure: SECURE,
});
// Extract <token> from "Authorization: Bearer <token>" header
// Not currently used. May be used in the future.
function parseAuthorizationToken(header: string): string | null {
const bearerTokenPattern = new RegExp("^ ?Bearer (?<token>[S]+)$");
const tokenMatch = bearerTokenPattern.exec(header);
if (
tokenMatch === null ||
tokenMatch.groups === undefined ||
tokenMatch.groups.token === undefined
)
return null;
return tokenMatch.groups.token;
}
function headerIsString(
header: ReturnType<ClientRequest["getHeader"]>
): header is string {
return typeof header === "string";
}
/**
* Check whether the request comes from OSMP server.
* Conditions:
* - "X-Gene42-Secret" header is present, is a string,
* and matches the EXPECTED_GENE42_SECRET environment variable.
*/
function isOSMPRequest(proxyReq: ClientRequest) {
// Return whether this request is an authorized request from the OSMP server
const gene42SecretHeader = proxyReq.getHeader("X-Gene42-Secret");
if (process.env.NODE_ENV === "development") {
console.log(`
[isOSMPRequest]
[X-Gene42-Secret Header]
Expected: ${EXPECTED_GENE42_SECRET}
Actual: ${gene42SecretHeader}
`);
}
if (!headerIsString(gene42SecretHeader)) return false;
// Only OSMP has this secret
if (gene42SecretHeader !== EXPECTED_GENE42_SECRET) return false;
return true;
}
/**
* Given a request to the proxy, determine if the request
* originated from OSMP, and modify the headers by replacing
* the Authorization header with one that is expected by PhenoTips.
*/
function modifyOSMPRequest(proxyReq: ClientRequest) {
if (isOSMPRequest(proxyReq)) {
proxyReq.removeHeader("Authorization");
proxyReq.setHeader("Authorization", `Basic ${PT_AUTHORIZATION}`);
}
}
// ##### PROXY FUNCTIONS #####
/**
* Primary operation of this proxy: modifying requests from OSMP to swap Authorization header
*/
proxy.on("proxyReq", function (proxyReq, req, res, options) {
if (process.env.NODE_ENV === "development") {
console.log("[MAIN proxyReq function]");
console.log("Headers Before:");
console.log(proxyReq.getHeaders());
modifyOSMPRequest(proxyReq);
console.log("Headers after:");
console.log(proxyReq.getHeaders());
} else {
modifyOSMPRequest(proxyReq);
}
});
/**
* Error logging function for proxy.
*/
proxy.on("error", function (err, req, res) {
res.end(`
Something went wrong while proxying request. Please contact your system administrator.
${err.name}
${err.message}
`);
console.error(`ERROR: ${err.name} - '${err.message}'`);
console.error(err.stack);
});
// Additional handlers for dev logging only
// if (process.env.NODE_ENV === "development") {
// proxy.on("proxyReq", function (proxyReq, req, res, options) {
// console.log("##### proxyReq #####");
// console.log(proxyReq);
// });
// proxy.on("proxyRes", function (proxyRes, req, res) {
// console.log("##### proxyRes #####");
// console.log(proxyRes);
// });
// }
/**
* Log message on proxy start.
*/
proxy.on("start", () => {
console.log(
`OSMP-CMH Proxy listening to ${LISTEN_HOST} on port ${LISTEN_PORT}. Targetting 'http${
SECURE ? "s" : ""
}://${TARGET_HOST}:${TARGET_PORT}'...`
);
});
// Types for this are wrong. proxy.listen takes port AND hostname.
// @ts-ignore
proxy.listen(LISTEN_PORT, LISTEN_HOST);