Session manager modified to catch all requests. Login redirect fix
[osm/UI.git] / skyquake / framework / core / api_utils / utils.js
1 /*
2 *
3 * Copyright 2016 RIFT.IO Inc
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18 // Helper Functions
19
20
21 /**
22 * Utils for use across the api_server.
23 * @module framework/core/utils
24 */
25
26 var fs = require('fs');
27 var request = require('request');
28 var Promise = require('promise');
29 var CONSTANTS = require('./constants.js');
30 var CONFD_PORT = '8008';
31 var APIVersion = '/v1';
32 var _ = require('lodash');
33
34 var requestWrapper = function(request) {
35 if (process.env.HTTP_PROXY && process.env.HTTP_PROXY != '') {
36 request = request.defaults({
37 'proxy': process.env.HTTP_PROXY
38 });
39 }
40 return request;
41 }
42
43 var confdPort = function(api_server) {
44 try {
45 api_server = api_server.replace(api_server.match(/[0-9](:[0-9]+)/)[1], '')
46 } catch (e) {
47
48 }
49 return api_server + ':' + CONFD_PORT;
50 };
51
52 var projectContextUrl = function(req, url) {
53 //NOTE: We need to go into the sessionStore because express-session
54 // does not reliably update the session.
55 // See https://github.com/expressjs/session/issues/450
56 var projectId = (req.session &&
57 req.sessionStore &&
58 req.sessionStore.sessions &&
59 req.sessionStore.sessions[req.session.id] &&
60 JSON.parse(req.sessionStore.sessions[req.session.id])['projectId']) ||
61 (null);
62 if (projectId) {
63 return url.replace(/(\/api\/operational\/|\/api\/config\/)(.*)/, '$1project/' + projectId + '/$2');
64 }
65 return url;
66 }
67
68 var addProjectContextToRPCPayload = function(req, url, inputPayload) {
69 //NOTE: We need to go into the sessionStore because express-session
70 // does not reliably update the session.
71 // See https://github.com/expressjs/session/issues/450
72 var projectId = (req.session &&
73 req.sessionStore &&
74 req.sessionStore.sessions &&
75 req.sessionStore.sessions[req.session.id] &&
76 JSON.parse(req.sessionStore.sessions[req.session.id])['projectId']) ||
77 (null);
78 if (projectId) {
79 if (url.indexOf('/api/operations/')) {
80 inputPayload['project-name'] = projectId;
81 }
82 }
83 return inputPayload;
84 }
85
86
87 var validateResponse = function(callerName, error, response, body, resolve, reject) {
88 var res = {};
89
90 if (error) {
91 console.log('Problem with "', callerName, '": ', error);
92 res.statusCode = 500;
93 res.errorMessage = {
94 error: 'Problem with ' + callerName + ': ' + error
95 };
96 reject(res);
97 return false;
98 } else if (response.statusCode >= CONSTANTS.HTTP_RESPONSE_CODES.ERROR.BAD_REQUEST) {
99 console.log('Problem with "', callerName, '": ', response.statusCode, ':', body);
100 res.statusCode = response.statusCode;
101
102 // auth specific
103 if (response.statusCode == CONSTANTS.HTTP_RESPONSE_CODES.ERROR.UNAUTHORIZED) {
104 res.errorMessage = {
105 error: 'Authentication needed' + body
106 };
107 reject(res);
108 return false;
109 }
110
111 res.errorMessage = {
112 error: 'Problem with ' + callerName + ': ' + response.statusCode + ': ' + typeof(body) == 'string' ? body : JSON.stringify(body),
113 body: body
114 };
115
116 reject(res);
117 return false;
118 } else if (response.statusCode == CONSTANTS.HTTP_RESPONSE_CODES.SUCCESS.NO_CONTENT) {
119 resolve({
120 statusCode: response.statusCode,
121 data: {}
122 });
123 return false;
124 } else {
125 return true;
126 }
127 };
128
129
130 var checkAuthorizationHeader = function(req) {
131 return new Promise(function(resolve, reject) {
132 if (req.session && req.session.authorization == null) {
133 reject();
134 } else {
135 resolve();
136 }
137 });
138 };
139
140 if (process.env.LOG_REQUESTS) {
141 var logFile = process.env.REQUESTS_LOG_FILE;
142
143 if (logFile && logFile != '') {
144 validateResponse = function(callerName, error, response, body, resolve, reject) {
145 var res = {};
146
147 if (error) {
148 console.log('Problem with "', callerName, '": ', error);
149 res.statusCode = 500;
150 res.errorMessage = {
151 error: 'Problem with ' + callerName + ': ' + error
152 };
153 reject(res);
154 fs.appendFileSync(logFile, 'Request API: ' + response.request.uri.href + ' ; ' + 'Error: ' + error);
155 return false;
156 } else if (response.statusCode >= CONSTANTS.HTTP_RESPONSE_CODES.ERROR.BAD_REQUEST) {
157 console.log('Problem with "', callerName, '": ', response.statusCode, ':', body);
158 res.statusCode = response.statusCode;
159
160 // auth specific
161 if (response.statusCode == CONSTANTS.HTTP_RESPONSE_CODES.ERROR.UNAUTHORIZED) {
162 res.errorMessage = {
163 error: 'Authentication needed' + body
164 };
165 reject(res);
166 fs.appendFileSync(logFile, 'Request API: ' + response.request.uri.href + ' ; ' + 'Error Body: ' + body);
167 return false;
168 }
169
170 res.errorMessage = {
171 error: 'Problem with ' + callerName + ': ' + response.statusCode + ': ' + body
172 };
173
174 reject(res);
175 fs.appendFileSync(logFile, 'Request API: ' + response.request.uri.href + ' ; ' + 'Error Body: ' + body);
176 return false;
177 } else if (response.statusCode == CONSTANTS.HTTP_RESPONSE_CODES.SUCCESS.NO_CONTENT) {
178 resolve();
179 fs.appendFileSync(logFile, 'Request API: ' + response.request.uri.href + ' ; ' + 'Response Body: ' + body);
180 return false;
181 } else {
182 fs.appendFileSync(logFile, 'Request API: ' + response.request.uri.href + ' ; ' + 'Response Body: ' + body);
183 return true;
184 }
185 };
186 }
187 }
188
189 /**
190 * Serve the error response back back to HTTP requester
191 * @param {Object} error - object of the format
192 * {
193 * statusCode - HTTP code to respond back with
194 * error - actual error JSON object to serve
195 * }
196 * @param {Function} res - a handle to the express response function
197 */
198 var sendErrorResponse = function(error, res) {
199 if (!error.statusCode) {
200 console.error('Status Code has not been set in error object: ', error);
201 }
202 res.status(error.statusCode);
203 res.send(error);
204 }
205
206 /**
207 * Serve the success response back to HTTP requester
208 * @param {Object} response - object of the format
209 * {
210 * statusCode - HTTP code to respond back with
211 * data - actual data JSON object to serve
212 * }
213 * @param {Function} res - a handle to the express response function
214 */
215 var sendSuccessResponse = function(response, res) {
216 res.status(response.statusCode);
217 res.send(response.data);
218 }
219
220 var passThroughConstructor = function(app) {
221 app.get('/passthrough/:type/*', function(req, res) {
222 var url = req.params[0];
223 var type = req.params.type;
224 var api_server = req.query["api_server"];
225 var uri = confdPort(api_server) + APIVersion + '/' + url + '?deep';
226 // Check that type is valid
227 switch (type) {
228 case 'data':
229 ;
230 case 'collection':
231 break;
232 default:
233 res.send({});
234 }
235 new Promise(function(resolve, reject) {
236 request({
237 uri: projectContextUrl(req, uri),
238 method: 'GET',
239 headers: _.extend({}, CONSTANTS.HTTP_HEADERS.accept[type], {
240 'Authorization': req.session && req.session.authorization,
241 forever: CONSTANTS.FOREVER_ON,
242 rejectUnauthorized: false,
243 })
244 }, function(error, response, body) {
245 if (validateResponse('Passthrough: ' + url, error, response, body, resolve, reject)) {
246 resolve(JSON.parse(response.body))
247 };
248 });
249 }).then(function(data) {
250 res.send(data);
251 }, function(error) {
252 res.send({'error': error, uri: uri})
253 });;
254 });
255 }
256
257 var getPortForProtocol = function(protocol) {
258 switch (protocol) {
259 case 'http':
260 return 8000;
261 case 'https':
262 return 8443;
263 }
264 }
265
266 module.exports = {
267 /**
268 * Ensure confd port is on api_server variable.
269 **/
270 confdPort: confdPort,
271
272 validateResponse: validateResponse,
273
274 checkAuthorizationHeader: checkAuthorizationHeader,
275
276 request: requestWrapper.call(null, request),
277
278 sendErrorResponse: sendErrorResponse,
279
280 sendSuccessResponse: sendSuccessResponse,
281
282 passThroughConstructor: passThroughConstructor,
283
284 getPortForProtocol: getPortForProtocol,
285
286 projectContextUrl: projectContextUrl,
287
288 addProjectContextToRPCPayload: addProjectContextToRPCPayload
289 };