@manyos/smileconnect-api 1.31.1 → 1.34.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.
- package/Dockerfile +9 -5
- package/app.js +22 -2
- package/conf/clients.json +4 -2
- package/controller/taskController.js +1 -1
- package/controller/ticketController.js +1 -0
- package/docs/general/config.md +9 -0
- package/docs/openapi.json +173 -1
- package/package.json +4 -4
- package/util/auth.js +15 -1
package/Dockerfile
CHANGED
|
@@ -2,20 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
# We label our stage as 'builder'
|
|
4
4
|
FROM node:12.14 as builder
|
|
5
|
-
|
|
5
|
+
ARG NPM_TOKEN
|
|
6
6
|
COPY package.json package-lock.json ./
|
|
7
7
|
|
|
8
8
|
RUN npm set progress=false && npm config set depth 0 && npm cache clean --force
|
|
9
9
|
|
|
10
10
|
## Storing node modules on a separate layer will prevent unnecessary npm installs at each build
|
|
11
|
-
RUN
|
|
12
|
-
|
|
11
|
+
RUN echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc && \
|
|
12
|
+
npm install && \
|
|
13
|
+
rm ~/.npmrc
|
|
14
|
+
RUN rm -f .npmrc
|
|
13
15
|
WORKDIR /home/node/app
|
|
14
16
|
|
|
15
17
|
COPY . .
|
|
16
18
|
|
|
17
|
-
## Build the angular app in production mode and store the artifacts in dist folder
|
|
18
|
-
|
|
19
19
|
EXPOSE 3000
|
|
20
20
|
|
|
21
|
+
RUN groupadd -g 999 appuser && \
|
|
22
|
+
useradd -r -u 999 -g appuser appuser
|
|
23
|
+
USER appuser
|
|
24
|
+
|
|
21
25
|
CMD npm start
|
package/app.js
CHANGED
|
@@ -11,6 +11,7 @@ const JwtStrategy = require('passport-jwt').Strategy,
|
|
|
11
11
|
const bodyParser = require('body-parser');
|
|
12
12
|
|
|
13
13
|
const config = require('./util/config');
|
|
14
|
+
const authUtil = require('./util/auth');
|
|
14
15
|
|
|
15
16
|
const cors = require('cors');
|
|
16
17
|
|
|
@@ -47,6 +48,8 @@ const https = require('https');
|
|
|
47
48
|
|
|
48
49
|
const maxHTTPSockets = process.env.MAX_HTTP_SOCKETS || 10;
|
|
49
50
|
|
|
51
|
+
const SSO_CLIENTNAME_ATTRIBUTE = process.env.SSO_CLIENTNAME_ATTRIBUTE || 'azp';
|
|
52
|
+
|
|
50
53
|
http.globalAgent.maxSockets = maxHTTPSockets;
|
|
51
54
|
https.globalAgent.maxSockets = maxHTTPSockets;
|
|
52
55
|
|
|
@@ -81,6 +84,8 @@ if (audienceArray.length > 0) {
|
|
|
81
84
|
opts.audience = audienceArray;
|
|
82
85
|
}
|
|
83
86
|
|
|
87
|
+
opts.passReqToCallback = true
|
|
88
|
+
|
|
84
89
|
// Do any necessary shutdown logic for our application here
|
|
85
90
|
const shutdown = (signal, value) => {
|
|
86
91
|
console.log("shutdown!");
|
|
@@ -128,22 +133,32 @@ app.use(compression()); //Compress all routes
|
|
|
128
133
|
|
|
129
134
|
log.debug('Passport Opts', opts);
|
|
130
135
|
passport.use(
|
|
131
|
-
new JwtStrategy(opts, function (jwt_payload, done) {
|
|
136
|
+
new JwtStrategy(opts, function (req, jwt_payload, done) {
|
|
132
137
|
//log.info(jwt_payload);
|
|
133
138
|
//log.info('token', jwt_payload.sub);
|
|
134
139
|
//TODO: Config error abfangen
|
|
135
140
|
//TODO: Add AdminScope and Impersonate
|
|
141
|
+
let clientId = jwt_payload[SSO_CLIENTNAME_ATTRIBUTE];
|
|
142
|
+
|
|
143
|
+
//check for master client
|
|
144
|
+
const requestedClientId = req.query.clientId
|
|
145
|
+
if (requestedClientId && authUtil.isMasterClient(clientId)) {
|
|
146
|
+
log.debug(`client ${clientId} acts as ${requestedClientId}`)
|
|
147
|
+
clientId = requestedClientId
|
|
148
|
+
}
|
|
149
|
+
|
|
136
150
|
const user = {
|
|
137
151
|
'id': jwt_payload.sub,
|
|
138
152
|
'azp': jwt_payload.azp,
|
|
139
153
|
'scope': jwt_payload.scope,
|
|
140
154
|
'exp': jwt_payload.exp,
|
|
141
|
-
'config': config.getClientConfig(
|
|
155
|
+
'config': config.getClientConfig(clientId),
|
|
142
156
|
'username': jwt_payload.preferred_username
|
|
143
157
|
}
|
|
144
158
|
log.debug('Passport User', jwt_payload);
|
|
145
159
|
const resource_access = jwt_payload.resource_access;
|
|
146
160
|
log.debug('User Resource Access', resource_access);
|
|
161
|
+
//TODO Prüfen ob das auch per
|
|
147
162
|
if (resource_access !== null && resource_access !== undefined && resource_access[user.azp] !== null && resource_access[user.azp] !== undefined) {
|
|
148
163
|
user.accessRoles = resource_access[user.azp].roles;
|
|
149
164
|
}
|
|
@@ -156,6 +171,11 @@ passport.use(
|
|
|
156
171
|
app.use(bodyParser.json({limit: '200mb'}));
|
|
157
172
|
app.use(bodyParser.urlencoded({limit: '200mb', extended: true}));
|
|
158
173
|
|
|
174
|
+
//health check
|
|
175
|
+
app.use('/v1/health', function (req, res, next) {
|
|
176
|
+
res.json({status:"ok"})
|
|
177
|
+
})
|
|
178
|
+
|
|
159
179
|
const maxFilesize = process.env.MAX_FILESIZE || 5;
|
|
160
180
|
|
|
161
181
|
app.use(fileUpload({
|
package/conf/clients.json
CHANGED
|
@@ -112,7 +112,7 @@ async function createTask(clientConfig, rootForm, rootRequestId, taskData, creat
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
|
-
|
|
115
|
+
//Todo make dynamic
|
|
116
116
|
async function createWorklog(clientConfig, taskId, summary, text, attachment) {
|
|
117
117
|
const task = await getTask(clientConfig,taskId)
|
|
118
118
|
const fields = clientConfig.taskWorklog.fields;
|
|
@@ -292,6 +292,7 @@ async function updateTicket(ticketConfig, clientConfig, id, ticketData) {
|
|
|
292
292
|
const myMapping = [{"oldName":ticketConfig.requestIdField, "newName" : "internalId"}];
|
|
293
293
|
const ticket = await getTicket(ticketConfig, myClientConfig, id, myMapping);
|
|
294
294
|
log.debug('Ticket to Update', ticket);
|
|
295
|
+
//todo Abfangen wenn getTicket nichts liefert.
|
|
295
296
|
const mapping = config.getMapping(ticketConfig.requestType);
|
|
296
297
|
|
|
297
298
|
//Constants work only on new.
|
package/docs/general/config.md
CHANGED
|
@@ -82,6 +82,15 @@ List of users who are allowed to access /v1/appconfig endpoints.
|
|
|
82
82
|
Sample:
|
|
83
83
|
ADMIN_USERS=username1, username2
|
|
84
84
|
|
|
85
|
+
### MASTER_CLIENTS
|
|
86
|
+
|
|
87
|
+
List of clients that can act on behalf of other clients.
|
|
88
|
+
|
|
89
|
+
The URL Parameter *clientId* is used for this.
|
|
90
|
+
|
|
91
|
+
Sample:
|
|
92
|
+
MASTER_CLIENTS=idm,adminTool
|
|
93
|
+
|
|
85
94
|
## Cache
|
|
86
95
|
|
|
87
96
|
### CACHETTL_CMDB
|
package/docs/openapi.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"openapi": "3.0.2",
|
|
3
3
|
"info": {
|
|
4
4
|
"title": "SMILEconnect",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.29.1",
|
|
6
6
|
"contact": {
|
|
7
7
|
"name": "manyos technology GmbH",
|
|
8
8
|
"url": "https://manyos.it",
|
|
@@ -3401,6 +3401,119 @@
|
|
|
3401
3401
|
},
|
|
3402
3402
|
"summary": "Create a new Client"
|
|
3403
3403
|
}
|
|
3404
|
+
},
|
|
3405
|
+
"/scripts": {
|
|
3406
|
+
"get": {
|
|
3407
|
+
"tags": [
|
|
3408
|
+
"Scripts"
|
|
3409
|
+
],
|
|
3410
|
+
"responses": {
|
|
3411
|
+
"200": {
|
|
3412
|
+
"content": {
|
|
3413
|
+
"application/json": {
|
|
3414
|
+
"schema": {
|
|
3415
|
+
"type": "array",
|
|
3416
|
+
"items": {
|
|
3417
|
+
"$ref": "#/components/schemas/script"
|
|
3418
|
+
}
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3421
|
+
},
|
|
3422
|
+
"description": "Returns an array with all scripts"
|
|
3423
|
+
}
|
|
3424
|
+
},
|
|
3425
|
+
"summary": "Get all scripts"
|
|
3426
|
+
},
|
|
3427
|
+
"post": {
|
|
3428
|
+
"requestBody": {
|
|
3429
|
+
"content": {
|
|
3430
|
+
"application/json": {
|
|
3431
|
+
"schema": {
|
|
3432
|
+
"$ref": "#/components/schemas/script"
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
},
|
|
3436
|
+
"required": true
|
|
3437
|
+
},
|
|
3438
|
+
"tags": [
|
|
3439
|
+
"Scripts"
|
|
3440
|
+
],
|
|
3441
|
+
"responses": {
|
|
3442
|
+
"200": {
|
|
3443
|
+
"$ref": "#/components/responses/ScriptDefinitionResponse"
|
|
3444
|
+
}
|
|
3445
|
+
},
|
|
3446
|
+
"summary": "Create a new script",
|
|
3447
|
+
"description": "Create a new script that is accessible by all catalog items."
|
|
3448
|
+
}
|
|
3449
|
+
},
|
|
3450
|
+
"/scripts/{scriptId}": {
|
|
3451
|
+
"get": {
|
|
3452
|
+
"tags": [
|
|
3453
|
+
"Scripts"
|
|
3454
|
+
],
|
|
3455
|
+
"responses": {
|
|
3456
|
+
"200": {
|
|
3457
|
+
"content": {
|
|
3458
|
+
"application/json": {
|
|
3459
|
+
"schema": {
|
|
3460
|
+
"$ref": "#/components/schemas/script"
|
|
3461
|
+
}
|
|
3462
|
+
}
|
|
3463
|
+
},
|
|
3464
|
+
"description": "Return a script"
|
|
3465
|
+
}
|
|
3466
|
+
},
|
|
3467
|
+
"summary": "Get a global script"
|
|
3468
|
+
},
|
|
3469
|
+
"put": {
|
|
3470
|
+
"requestBody": {
|
|
3471
|
+
"content": {
|
|
3472
|
+
"application/json": {
|
|
3473
|
+
"schema": {
|
|
3474
|
+
"$ref": "#/components/schemas/script"
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3477
|
+
},
|
|
3478
|
+
"required": true
|
|
3479
|
+
},
|
|
3480
|
+
"tags": [
|
|
3481
|
+
"Scripts"
|
|
3482
|
+
],
|
|
3483
|
+
"responses": {
|
|
3484
|
+
"200": {
|
|
3485
|
+
"$ref": "#/components/responses/ScriptDefinitionResponse"
|
|
3486
|
+
}
|
|
3487
|
+
},
|
|
3488
|
+
"summary": "Update a script",
|
|
3489
|
+
"description": "Updates a existing script that is accessible by all catalog item."
|
|
3490
|
+
},
|
|
3491
|
+
"delete": {
|
|
3492
|
+
"tags": [
|
|
3493
|
+
"Scripts"
|
|
3494
|
+
],
|
|
3495
|
+
"responses": {
|
|
3496
|
+
"200": {
|
|
3497
|
+
"$ref": "#/components/responses/DeleteResponse"
|
|
3498
|
+
}
|
|
3499
|
+
},
|
|
3500
|
+
"summary": "Delete this script"
|
|
3501
|
+
},
|
|
3502
|
+
"parameters": [
|
|
3503
|
+
{
|
|
3504
|
+
"examples": {
|
|
3505
|
+
"catalogIdExample": {
|
|
3506
|
+
"value": "serverRestart"
|
|
3507
|
+
}
|
|
3508
|
+
},
|
|
3509
|
+
"name": "scriptId",
|
|
3510
|
+
"schema": {
|
|
3511
|
+
"type": "string"
|
|
3512
|
+
},
|
|
3513
|
+
"in": "path",
|
|
3514
|
+
"required": true
|
|
3515
|
+
}
|
|
3516
|
+
]
|
|
3404
3517
|
}
|
|
3405
3518
|
},
|
|
3406
3519
|
"components": {
|
|
@@ -15130,6 +15243,25 @@
|
|
|
15130
15243
|
"summary": "Email Password Reset",
|
|
15131
15244
|
"notes": "Reset the password for the email system"
|
|
15132
15245
|
}
|
|
15246
|
+
},
|
|
15247
|
+
"script": {
|
|
15248
|
+
"title": "Root Type for script",
|
|
15249
|
+
"description": "Scripts can be executed to gather data or perform actions.\n\nScripts always need to end with resolve() or reject().\n\nresolve(data) is called when the script execution was successful. Data will be returned as scriptResult. reject(error) is called when the script was not successful and an error needs to be returned.",
|
|
15250
|
+
"type": "object",
|
|
15251
|
+
"properties": {
|
|
15252
|
+
"id": {
|
|
15253
|
+
"description": "id of the script",
|
|
15254
|
+
"type": "string"
|
|
15255
|
+
},
|
|
15256
|
+
"code": {
|
|
15257
|
+
"description": "the code of the script",
|
|
15258
|
+
"type": "string"
|
|
15259
|
+
}
|
|
15260
|
+
},
|
|
15261
|
+
"example": {
|
|
15262
|
+
"id": "ars101",
|
|
15263
|
+
"code": "try {\n const result = await adapter.remedy.search(params.form, params.query, params.valueField + ',' + params.labelField);\n const data = result.data.map(item => {\n return {\n \"value\":item[params.valueField],\n \"label\":item[params.labelField],\n }\n });\n resolve(data);\n} catch (error) {\n reject(error);\n}"
|
|
15264
|
+
}
|
|
15133
15265
|
}
|
|
15134
15266
|
},
|
|
15135
15267
|
"responses": {
|
|
@@ -15185,6 +15317,42 @@
|
|
|
15185
15317
|
}
|
|
15186
15318
|
},
|
|
15187
15319
|
"description": "Get the attachment of a worklog."
|
|
15320
|
+
},
|
|
15321
|
+
"ScriptDefinitionResponse": {
|
|
15322
|
+
"content": {
|
|
15323
|
+
"application/json": {
|
|
15324
|
+
"schema": {
|
|
15325
|
+
"$ref": "#/components/schemas/script"
|
|
15326
|
+
}
|
|
15327
|
+
}
|
|
15328
|
+
},
|
|
15329
|
+
"description": "Returns a script definition"
|
|
15330
|
+
},
|
|
15331
|
+
"DeleteResponse": {
|
|
15332
|
+
"content": {
|
|
15333
|
+
"application/json": {
|
|
15334
|
+
"schema": {
|
|
15335
|
+
"type": "object",
|
|
15336
|
+
"properties": {
|
|
15337
|
+
"id": {
|
|
15338
|
+
"type": "string"
|
|
15339
|
+
},
|
|
15340
|
+
"status": {
|
|
15341
|
+
"type": "string"
|
|
15342
|
+
}
|
|
15343
|
+
}
|
|
15344
|
+
},
|
|
15345
|
+
"examples": {
|
|
15346
|
+
"DeletionResponseExample": {
|
|
15347
|
+
"value": {
|
|
15348
|
+
"id": "serverRestart",
|
|
15349
|
+
"status": "deleted"
|
|
15350
|
+
}
|
|
15351
|
+
}
|
|
15352
|
+
}
|
|
15353
|
+
}
|
|
15354
|
+
},
|
|
15355
|
+
"description": "Returns deletion confirmation for the requested item"
|
|
15188
15356
|
}
|
|
15189
15357
|
},
|
|
15190
15358
|
"securitySchemes": {
|
|
@@ -15241,6 +15409,10 @@
|
|
|
15241
15409
|
{
|
|
15242
15410
|
"name": "Templates",
|
|
15243
15411
|
"description": ""
|
|
15412
|
+
},
|
|
15413
|
+
{
|
|
15414
|
+
"name": "Scripts",
|
|
15415
|
+
"description": ""
|
|
15244
15416
|
}
|
|
15245
15417
|
]
|
|
15246
15418
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@manyos/smileconnect-api",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.34.0",
|
|
4
4
|
"description": "A proxy and abstraction layer for BMCs IT Service Management Suite",
|
|
5
5
|
"main": "app.js",
|
|
6
6
|
"scripts": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"author": "Robert Hannemann",
|
|
12
12
|
"license": "ISC",
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@manyos/adapter-foundation": "^1.1.
|
|
14
|
+
"@manyos/adapter-foundation": "^1.1.2",
|
|
15
15
|
"@manyos/logger": "^1.3.0",
|
|
16
16
|
"bunyan": "^1.8.15",
|
|
17
17
|
"bunyan-express-serializer": "^1.0.0",
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
"express-fileupload": "^1.2.1",
|
|
23
23
|
"express-rate-limit": "^5.2.6",
|
|
24
24
|
"express-request-id": "^1.4.1",
|
|
25
|
-
"express-validator": "^6.10.
|
|
25
|
+
"express-validator": "^6.10.1",
|
|
26
26
|
"moment": "^2.29.1",
|
|
27
|
-
"mongoose": "^5.12.
|
|
27
|
+
"mongoose": "^5.12.5",
|
|
28
28
|
"node-cache": "^4.2.1",
|
|
29
29
|
"node-fetch": "^2.6.1",
|
|
30
30
|
"only": "0.0.2",
|
package/util/auth.js
CHANGED
|
@@ -19,6 +19,20 @@ function isAuthorizedAdmin(req, res, next) {
|
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
function isMasterClient(clientId) {
|
|
23
|
+
const masterClients = process.env.MASTER_CLIENTS;
|
|
24
|
+
if (masterClients !== null
|
|
25
|
+
&& masterClients !== undefined
|
|
26
|
+
&& clientId !== null
|
|
27
|
+
&& clientId !== undefined
|
|
28
|
+
&& isUserInList(masterClients, clientId)) {
|
|
29
|
+
log.debug('master client authorized', clientId);
|
|
30
|
+
return true
|
|
31
|
+
} else {
|
|
32
|
+
return false
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
22
36
|
function isUserInList(userList, userName) {
|
|
23
37
|
log.debug('Check if user is in List', userName, userList)
|
|
24
38
|
if (userList !== null && userList !== undefined && userName !== null && userName !== undefined) {
|
|
@@ -33,5 +47,5 @@ function isUserInList(userList, userName) {
|
|
|
33
47
|
}
|
|
34
48
|
|
|
35
49
|
module.exports = {
|
|
36
|
-
isAuthorizedAdmin
|
|
50
|
+
isAuthorizedAdmin, isMasterClient
|
|
37
51
|
}
|