-
Notifications
You must be signed in to change notification settings - Fork 81
/
app.js
executable file
·305 lines (263 loc) · 10.1 KB
/
app.js
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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
const body_parser = require('body-parser');
const useragent = require('express-useragent');
const clc = require('cli-color');
const cookie_parser = require('cookie-parser');
const cors = require('cors');
const debug = require('debug')('idm:app');
const express = require('express');
const favicon = require('serve-favicon');
const force_ssl = require('express-force-ssl');
const i18n = require('i18n-express');
const logger = require('morgan');
const method_override = require('method-override');
const partials = require('express-partials');
const path = require('path');
const sass_middleware = require('./lib/node-sass-middleware');
const session = require('cookie-session');
const package_info = require('./package.json');
const fs = require('fs');
const extparticipant = require('./controllers/extparticipant/extparticipant');
const version = require('./version.json');
version.keyrock.version = package_info.version;
version.keyrock.doc = package_info.homepage;
fs.stat('./package.json', function (err, stats) {
version.keyrock.release_date = stats.mtime;
});
// Obtain secret from config file
const config_service = require('./lib/configService.js');
const config = config_service.get_config();
// Create vars that store routes
const index = require('./routes/web/index');
const api = require('./routes/api/index');
const oauth2 = require('./routes/oauth2/oauth2');
const saml2 = require('./routes/saml2/saml2');
const authregistry = require('./routes/authregistry/authregistry');
const oauth2_controller = require('./controllers/oauth2/oauth2');
const translation_merger = require('./lib/json_directory_merger');
const app = express();
const helmet = require('helmet');
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
// Set logs in development
if (config.debug) {
app.use(logger('dev'));
}
// img-src 'self' data:image/png
// Disabled header
app.disable('x-powered-by');
// Set security headers
const csp_default = {
directives: {
defaultSrc: ["'self'", 'data:'], // eslint-disable-line snakecase/snakecase
fontSrc: ["'self'", 'data:', 'https://fonts.gstatic.com'], // eslint-disable-line snakecase/snakecase
imgSrc: ["'self'", 'data'], // eslint-disable-line snakecase/snakecase
scriptSrc: ["'self'", "'unsafe-inline'"], // eslint-disable-line snakecase/snakecase
styleSrc: ["'self'", 'https:', "'unsafe-inline'", 'https://fonts.googleapis.com'] // eslint-disable-line snakecase/snakecase
},
reportOnly: false // eslint-disable-line snakecase/snakecase
};
const csp_disabled = {
directives: {
defaultSrc: ['*'], // eslint-disable-line snakecase/snakecase
fontSrc: ['*'], // eslint-disable-line snakecase/snakecase
imgSrc: ['*'], // eslint-disable-line snakecase/snakecase
scriptSrc: ['*'], // eslint-disable-line snakecase/snakecase
styleSrc: ['*'] // eslint-disable-line snakecase/snakecase
},
reportOnly: true // eslint-disable-line snakecase/snakecase
};
const csp_options = config.https.enabled ? csp_default : csp_disabled;
if (config.csp.form_action) {
csp_options.directives.formAction = config.csp.form_action;
}
if (config.csp.script_src) {
csp_options.directives.scriptSrc = config.csp.script_src;
}
app.use(helmet.contentSecurityPolicy(csp_options));
app.use(
helmet.dnsPrefetchControl({
allow: process.env.IDM_DNS_PREFETCH_ALLOW === 'true'
})
);
app.use(helmet.expectCt());
app.use(
helmet.frameguard({
action: process.env.IDM_FRAMEGUARD_ACTION || 'sameorigin'
})
);
app.use(
helmet.hsts({
maxAge: process.env.IDM_HTTPS_MAX_AGE || 15552000, // eslint-disable-line snakecase/snakecase
includeSubDomains: process.env.IDM_INCLUDE_SUB_DOMAINS !== 'false', // eslint-disable-line snakecase/snakecase
preload: process.env.IDM_PRELOAD === 'true'
})
);
app.use(helmet.ieNoOpen());
app.use(helmet.noSniff());
// options.permittedPolicies is a string that must be "none",
//"master-only", "by-content-type", or "all". It defaults to "none".
app.use(
helmet.permittedCrossDomainPolicies({
permittedPolicies: process.env.IDM_PERMITTED_POLICIES || 'none' // eslint-disable-line snakecase/snakecase
})
);
app.use(helmet.referrerPolicy());
app.use(helmet.xssFilter());
// Parse request
app.use(body_parser.json({ limit: '50mb' }));
app.use(body_parser.urlencoded({ limit: '50mb', extended: true }));
// Parse user agent header
app.use(useragent.express());
// CORS Enable
if (config.cors.enabled) {
app.use(cors(config.cors.options));
}
// Set routes for version
const up_date = new Date();
app.use('/version', function (req, res) {
version.keyrock.uptime = require('./lib/time').ms_to_time(new Date() - up_date);
version.keyrock.api.link = config.host + '/' + version.keyrock.api.version;
res.status(200).send(version);
});
// The following middlewares are used by the GUI only. Not required in headless mode.
if (!config.headless) {
// uncomment after placing your favicon in /public
app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(partials());
app.use(cookie_parser(config.session.secret));
app.use(
session({
secret: config.session.secret,
name: 'session',
secure: config.https.enabled,
maxAge: config.session.expires // eslint-disable-line snakecase/snakecase
})
);
const styles = config.site.theme || 'default';
// Middleware to convert sass files to css
app.use(
sass_middleware({
src: path.join(__dirname, 'themes/' + styles),
dest: path.join(__dirname, 'public/stylesheets'),
debug: config.debug,
outputStyle: 'compressed', // eslint-disable-line snakecase/snakecase
prefix: '/stylesheets' // Where prefix is at <link rel="stylesheets" href="prefix/style.css"/>
})
);
app.use(express.static(path.join(__dirname, 'public')));
app.use(method_override('_method'));
const translation_path = path.join(__dirname, 'etc/translations/');
app.use(
translation_merger.init({
directory_1: translation_path,
directory_2: path.join(__dirname, 'themes/' + config.site.theme + '/translations/'),
merged_path: path.join(__dirname, 'public/', 'translations_merged')
}),
i18n({
translationsPath: translation_merger.get_merge_path(), // eslint-disable-line snakecase/snakecase
siteLangs: ['de', 'en', 'es', 'ja', 'ko'], // eslint-disable-line snakecase/snakecase
textsVarName: 'translation', // eslint-disable-line snakecase/snakecase
browserEnable: true, // eslint-disable-line snakecase/snakecase
defaultLang: config.lang.default_lang || 'en' // eslint-disable-line snakecase/snakecase
})
);
// Helpers dinamicos:
app.use(function (req, res, next) {
res.set('Cache-Control', 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0');
// init req.session.redir
if (!req.session.redir) {
req.session.redir = '/';
}
// To make visible req.session in the view
res.locals.session = req.session;
// {text: 'message text', type: 'info | success | warning | danger'}
res.locals.message = {};
// {text: 'message text', status: ''}
res.locals.error = {};
res.locals.site = config.site;
res.locals.fs = require('fs');
next();
});
}
// Force HTTPS connection to web server
if (config.https.enabled) {
app.set('forceSSLOptions', {
enable301Redirects: true, // eslint-disable-line snakecase/snakecase
trustXFPHeader: false, // eslint-disable-line snakecase/snakecase
httpsPort: config.https.port, // eslint-disable-line snakecase/snakecase
sslRequiredMessage: 'SSL Required.' // eslint-disable-line snakecase/snakecase
});
// Set routes for api
app.use('/v1', force_ssl, api);
app.use('/v3', force_ssl, api); // REDIRECT OLD KEYSTONE REQUESTS TO THE SAME API
// Set routes for oauth2
app.use('/oauth2', force_ssl, oauth2);
app.get('/user', force_ssl, oauth2_controller.authenticate_token);
// Set route for capabilities endpoint
app.use('/capabilities', force_ssl, extparticipant.capabilities);
if (config.authorization.level === 'payload') {
app.post('/pdp/open_policy_agent', force_ssl, oauth2_controller.auth_opa_policy);
app.post('/pdp/xacml', force_ssl, oauth2_controller.auth_xacml_policy);
}
// Set routes for saml2
app.use('/saml2', force_ssl, saml2);
// Set routes for the authorization registry if enabled
if (config.ar.url === 'internal') {
app.use('/ar', authregistry);
}
if (!config.headless) {
// The following routes are used by the GUI only. Not required in headless mode.
app.use('/', force_ssl, index);
} else {
app.get('/', function (req, res) {
res.status(501).json({
error: 'Keyrock instance is running in HEADLESS mode'
});
});
}
} else {
// Set routes for api
app.use('/v1', api);
app.use('/v3', api); // REDIRECT OLD KEYSTONE REQUESTS TO THE SAME API
// Set routes for oauth2
app.use('/oauth2', oauth2);
app.get('/user', oauth2_controller.authenticate_token);
// Set route for capabilities endpoint
app.use('/capabilities', extparticipant.capabilities);
if (config.authorization.level === 'payload') {
app.post('/pdp/open_policy_agent', oauth2_controller.auth_opa_policy);
app.post('/pdp/xacml', oauth2_controller.auth_xacml_policy);
}
// Set routes for saml2
app.use('/saml2', saml2);
// Set routes for the authorization registry if enabled
if (config.ar.url === 'internal') {
app.use('/ar', authregistry);
}
if (!config.headless) {
// The following routes are used by the GUI only. Not required in headless mode.
app.use('/', index);
} else {
app.get('/', function (req, res) {
res.status(501).json({
error: 'Keyrock instance is running in HEADLESS mode'
});
});
}
}
debug(
clc.green(config.headless ? 'Keyrock instance is clustered and running in HEADLESS mode' : 'Keyrock GUI is available')
);
// Check connection with Authzforce
if (config.authorization.authzforce.enabled) {
require('./lib/authzforce.js')
.check_connection()
.then(function (status) {
debug(clc.green('Connection with Authzforce: ' + status));
})
.catch(function (error) {
debug(clc.red(error));
});
}
module.exports = app;