Skip to content

Commit a32a24d

Browse files
authored
#186531662: Govern rules support (#21)
Support Governance Rules
1 parent a99ee14 commit a32a24d

File tree

7 files changed

+789
-232
lines changed

7 files changed

+789
-232
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,3 +153,4 @@ $RECYCLE.BIN/
153153

154154
### Less ###
155155
*.css
156+
*.tgz

lib/dataUtils.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,22 @@ function _safeJsonParse(body) {
141141
}
142142
}
143143

144+
function ensureToString(id) {
145+
if (typeof id === 'number') {
146+
return String(id);
147+
}
148+
if (typeof id === 'string') {
149+
return id;
150+
}
151+
if (id === null || id === undefined) {
152+
return id;
153+
}
154+
if (typeof id === 'object') {
155+
return String(id);
156+
}
157+
return id;
158+
}
159+
144160

145161
function _startWithJson(body) {
146162

@@ -213,11 +229,19 @@ function _getEventModelFromRequestandResponse(
213229
return logData;
214230
}
215231

232+
var logMessage = function(debug, functionName, message) {
233+
if (debug) {
234+
console.log('MOESIF: [' + functionName + '] ' + message);
235+
}
236+
};
237+
216238
module.exports = {
217239
getUrlFromRequestOptions: _getUrlFromRequestOptions,
218240
getEventModelFromRequestandResponse: _getEventModelFromRequestandResponse,
219241
safeJsonParse: _safeJsonParse,
220242
startWithJson: _startWithJson,
221243
bodyToBase64: _bodyToBase64,
222-
hashSensitive: _hashSensitive
244+
hashSensitive: _hashSensitive,
245+
logMessage: logMessage,
246+
ensureToString: ensureToString
223247
};
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
var requestIp = require('request-ip');
2+
var dataUtils = require('./dataUtils');
3+
var url = require('url');
4+
5+
var safeJsonParse = dataUtils.safeJsonParse;
6+
7+
function getIpAddress(event, context, isV1) {
8+
if (isV1) {
9+
return (
10+
requestIp.getClientIp(event) ||
11+
(event.requestContext &&
12+
event.requestContext.identity &&
13+
event.requestContext.identity.sourceIp)
14+
);
15+
} else {
16+
return (
17+
event.requestContext &&
18+
event.requestContext.http &&
19+
event.requestContext.http.sourceIp
20+
);
21+
}
22+
}
23+
24+
function getRequestBody(event, context, isV1) {
25+
const bodyWrapper = safeJsonParse(event.body);
26+
return bodyWrapper;
27+
}
28+
29+
function normalizeHeaders(headers) {
30+
if (!headers) {
31+
return headers;
32+
}
33+
try {
34+
const result = Object.assign({}, headers || {});
35+
Object.keys(headers).forEach(function (key) {
36+
const lowerCase = key.toLowerCase();
37+
result[lowerCase] = result[key];
38+
});
39+
return result;
40+
} catch (err) {
41+
return Object.assign({}, headers || {});
42+
}
43+
}
44+
45+
function getURLWithQueryStringParams(event) {
46+
try {
47+
var protocol = (event.headers && event.headers['X-Forwarded-Proto'] || event.headers['x-forwarded-proto']) ? (event.headers['X-Forwarded-Proto'] || event.headers['x-forwarded-proto']) : 'http';
48+
var host = event.headers.Host || event.headers.host;
49+
return url.format({ protocol: protocol, host: host, pathname: event.path, query: event.queryStringParameters });
50+
} catch (err) {
51+
return '/';
52+
}
53+
}
54+
55+
function getRequestUri(event, context, isV1) {
56+
if (isV1) {
57+
return getURLWithQueryStringParams(event);
58+
} else {
59+
const result = event.rawPath + (event.rawQueryString ? "?" + event.rawQueryString : "");
60+
return result;
61+
}
62+
}
63+
64+
function getRequestVerb(event, context, isV1) {
65+
if (isV1) {
66+
return event.httpMethod;
67+
} else {
68+
return event.requestContext &&
69+
event.requestContext.http &&
70+
event.requestContext.http.method;
71+
}
72+
}
73+
74+
function prepareRequestDataForGovernance(event, context, isV1) {
75+
const requestBody = getRequestBody(event, context, isV1);
76+
return {
77+
requestFields: {
78+
'request.verb': getRequestVerb(event, context, isV1),
79+
'request.ip': getIpAddress(event, context, isV1),
80+
'request.route': getRequestUri(event, context, isV1),
81+
'request.body.operationName': requestBody && requestBody.operationName,
82+
},
83+
requestHeaders: normalizeHeaders(event.headers || {}),
84+
requestBody
85+
};
86+
}
87+
88+
// in V2
89+
// https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
90+
// if there is no statusCode but valid JSON, API gateway will interpret
91+
// the response.
92+
function translateLambdaResultIfNeeded(result, isV1) {
93+
if (isV1) {
94+
return result || {};
95+
}
96+
97+
if (result && result.statusCode) {
98+
return result;
99+
}
100+
101+
if ((!result || !result.statusCode) && (typeof result === 'object' || typeof result === 'string' || typeof result === 'undefined')) {
102+
return {
103+
isBase64Encoded: false,
104+
statusCode: 200,
105+
body: typeof result === 'undefined' ? undefined : JSON.stringify(result),
106+
headers: {
107+
'content-type': 'application/json'
108+
}
109+
};
110+
}
111+
112+
return result || {};
113+
}
114+
115+
function mapResponseHeaders(event, context, result) {
116+
const headers = Object.assign({}, result.headers || {}); // NOTE: Mutating event.headers; prefer deep clone of event.headers
117+
return headers;
118+
}
119+
120+
function constructBaseLogData(
121+
event,
122+
context,
123+
err,
124+
result,
125+
options,
126+
isV1
127+
) {
128+
var logData = {};
129+
logData.request = {};
130+
logData.response = {};
131+
if (isV1) {
132+
logData.request.time =
133+
event && event.requestContext && event.requestContext.requestTimeEpoch
134+
? new Date(event.requestContext.requestTimeEpoch)
135+
: Date.now();
136+
} else {
137+
logData.request.time =
138+
event && event.requestContext && event.requestContext.timeEpoch
139+
? new Date(event.requestContext.timeEpoch)
140+
: Date.now();
141+
}
142+
143+
logData.request.uri = getRequestUri(event, context, isV1);
144+
logData.request.verb = getRequestVerb(event, context, isV1);
145+
146+
logData.request.apiVersion = options.getApiVersion(event, context);
147+
148+
logData.request.ipAddress = getIpAddress(event, context, isV1);
149+
150+
logData.request.headers = Object.assign({}, event.headers || {});
151+
logData.metadata = options.getMetadata(event, context);
152+
153+
if (options.logBody && event.body) {
154+
if (event.isBase64Encoded) {
155+
logData.request.body = event.body;
156+
logData.request.transferEncoding = "base64";
157+
} else {
158+
const bodyWrapper = safeJsonParse(event.body);
159+
logData.request.body = bodyWrapper.body;
160+
logData.request.transferEncoding = bodyWrapper.transferEncoding;
161+
}
162+
}
163+
164+
var safeRes = translateLambdaResultIfNeeded(result, isV1);
165+
166+
logData.response.time = Math.max(
167+
new Date(logData.request.time).getTime(),
168+
Date.now()
169+
);
170+
logData.response.status = safeRes.statusCode
171+
? parseInt(safeRes.statusCode)
172+
: 599;
173+
logData.response.headers = mapResponseHeaders(event, context, safeRes);
174+
if (logData.response.headers['X-Moesif-Blocked-By']) {
175+
logData.blockedBy = logData.response.headers['X-Moesif-Blocked-By'];
176+
}
177+
178+
if (options.logBody && safeRes.body) {
179+
if (safeRes.isBase64Encoded) {
180+
logData.response.body = safeRes.body;
181+
logData.response.transferEncoding = "base64";
182+
} else {
183+
const bodyWrapper = safeJsonParse(safeRes.body);
184+
logData.response.body = bodyWrapper.body;
185+
logData.response.transferEncoding = bodyWrapper.transferEncoding;
186+
}
187+
}
188+
189+
return logData;
190+
}
191+
192+
module.exports = {
193+
constructBaseLogData,
194+
prepareRequestDataForGovernance,
195+
}

0 commit comments

Comments
 (0)