From dd2bb1b37f5aaeeac9fbec4c7ff25461be2027d7 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Tue, 29 Jun 2021 18:45:21 +0530 Subject: [PATCH 001/856] Task54765 - Changes for Rbac --- logs/2021_06_29.log | 1 + nodemon/nodemon-igot-stage.json | 4 +- package-lock.json | 68 +++++++++- package.json | 3 + src/server.ts | 13 +- src/utils/apiWhiteList.ts | 232 ++++++++++++++++++++++++++++++++ src/utils/env.ts | 3 +- src/utils/fileLogger.ts | 21 +++ src/utils/proxyCreator.ts | 23 ++++ src/utils/requestExtract.ts | 6 +- src/utils/whitelistApis.ts | 49 +++++++ 11 files changed, 410 insertions(+), 13 deletions(-) create mode 100644 logs/2021_06_29.log create mode 100644 src/utils/apiWhiteList.ts create mode 100644 src/utils/fileLogger.ts create mode 100644 src/utils/whitelistApis.ts diff --git a/logs/2021_06_29.log b/logs/2021_06_29.log new file mode 100644 index 00000000..c3320ae7 --- /dev/null +++ b/logs/2021_06_29.log @@ -0,0 +1 @@ +{"level":30,"time":1624971983174,"pid":9772,"hostname":"DESKTOP-QH9UV1F","msg":"In WhilteList Call========/proxies/v8/api/user/v2/read"} diff --git a/nodemon/nodemon-igot-stage.json b/nodemon/nodemon-igot-stage.json index 39a05837..da5a3aeb 100644 --- a/nodemon/nodemon-igot-stage.json +++ b/nodemon/nodemon-igot-stage.json @@ -14,7 +14,7 @@ "ES_PASSWORD": "", "ES_USERNAME": "", "FALLBACK_APIS_TO_LEX": false, - "HTTPS_HOST": "https://igot-sunbird.idc.tarento.com", + "HTTPS_HOST": "https://igot-dev.in", "CERT_AUTH_TOKEN": "bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJRekw4VVA1dUtqUFdaZVpMd1ZtTFJvNHdqWTg2a2FrcSJ9.TPjV0xLacSbp3FbJ7XeqHoKFN35Rl4YHx3DZNN9pm0o", "IAP_CLIENT_SECRET": "qWEH1rbrFmDd3V6D", "IAP_CODE_API_BASE": "", @@ -26,7 +26,7 @@ "KEYCLOAK_ADMIN_PASSWORD": "admin", "KEYCLOAK_ADMIN_USERNAME": "admin", "KC_NEW_USER_DEFAULT_PWD": "User@123", - "MULTI_TENANT_KEYCLOAK": "igot;https://igot-sunbird.idc.tarento.com/auth;sunbird", + "MULTI_TENANT_KEYCLOAK": "igot;https://igot-dev.in/auth;sunbird", "LA_HOST_PROXY": "http://IP-ADDR", "NAVIGATOR_JSON_HOST": "http://IP-ADDR:3007/web-hosted/navigator/json", "NODE_API_BASE_2_CLIENT_ID": "admin", diff --git a/package-lock.json b/package-lock.json index ea9108e9..3060d82f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -728,6 +728,11 @@ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", "dev": true }, + "atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==" + }, "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", @@ -1726,6 +1731,11 @@ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.0.1.tgz", "integrity": "sha512-C14oTzTZy8DH1Eq8N78owrCWvf3+cnJw88BTK/N3DYWVxDJuJzPaNdplzYxDYuuXXGvqBcO4Vy5SOrwAooXSWw==" }, + "dateformat": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.5.1.tgz", + "integrity": "sha512-OD0TZ+B7yP7ZgpJf5K2DIbj3FZvFvxgFUuaqA/V5zTjAtAAXZ1E8bktHxmAGs4x5b7PflqA9LeQ84Og7wYtF7Q==" + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -2416,6 +2426,11 @@ "requires": { "ms": "2.0.0" } + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" } } }, @@ -2604,6 +2619,16 @@ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" }, + "fast-redact": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.0.1.tgz", + "integrity": "sha512-kYpn4Y/valC9MdrISg47tZOpYBNoTXKgT9GYXFpHN/jYFs+lFkPoisY+LcBODdKVMY96ATzvzsWv+ES/4Kmufw==" + }, + "fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" + }, "fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -2728,6 +2753,11 @@ "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", "dev": true }, + "flatstr": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/flatstr/-/flatstr-1.0.12.tgz", + "integrity": "sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw==" + }, "flush-write-stream": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", @@ -5463,9 +5493,9 @@ "dev": true }, "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.0.tgz", + "integrity": "sha512-f66KywYG6+43afgE/8j/GoiNyygk/bnoCbps++3ErRKsIYkGGupyv07R2Ok5m9i67Iqc+T2g1eAUGUPzWhYTyg==" }, "path-type": { "version": "1.1.0", @@ -5518,6 +5548,24 @@ "pinkie": "^2.0.0" } }, + "pino": { + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/pino/-/pino-6.11.3.tgz", + "integrity": "sha512-drPtqkkSf0ufx2gaea3TryFiBHdNIdXKf5LN0hTM82SXI4xVIve2wLwNg92e1MT6m3jASLu6VO7eGY6+mmGeyw==", + "requires": { + "fast-redact": "^3.0.0", + "fast-safe-stringify": "^2.0.7", + "flatstr": "^1.0.12", + "pino-std-serializers": "^3.1.0", + "quick-format-unescaped": "^4.0.3", + "sonic-boom": "^1.0.2" + } + }, + "pino-std-serializers": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-3.2.0.tgz", + "integrity": "sha512-EqX4pwDPrt3MuOAAUBMU0Tk5kR/YcCM5fNPEzgCO2zJ5HfX0vbiH9HbJglnyeQsN96Kznae6MWD47pZB5avTrg==" + }, "pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -5698,6 +5746,11 @@ "strict-uri-encode": "^1.0.0" } }, + "quick-format-unescaped": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.3.tgz", + "integrity": "sha512-MaL/oqh02mhEo5m5J2rwsVL23Iw2PEaGVHgT2vFt8AAsr0lfvQA5dpXo9TPu0rz7tSBdUPgkbam0j/fj5ZM8yg==" + }, "random-bytes": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", @@ -6438,6 +6491,15 @@ } } }, + "sonic-boom": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-1.4.1.tgz", + "integrity": "sha512-LRHh/A8tpW7ru89lrlkU4AszXt1dbwSjVWguGrmlxE7tawVmDBlI1PILMkXAxJTwqhgsEeTHzj36D5CmHgQmNg==", + "requires": { + "atomic-sleep": "^1.0.0", + "flatstr": "^1.0.12" + } + }, "sort-keys": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", diff --git a/package.json b/package.json index a7ac59ea..911f361f 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "cookie-parser": "^1.4.5", "cors": "^2.8.5", "date-fns": "^2.0.1", + "dateformat": "^4.5.1", "express": "^4.17.1", "express-fileupload": "^1.1.4", "express-healthcheck": "^0.1.0", @@ -42,6 +43,8 @@ "lodash": "^4.17.21", "morgan": "^1.9.1", "node-xlsx": "^0.15.0", + "path-to-regexp": "^6.2.0", + "pino": "^6.11.3", "request": "^2.88.0", "uuid": "^3.3.2" }, diff --git a/src/server.ts b/src/server.ts index e1461538..7a03d3ae 100644 --- a/src/server.ts +++ b/src/server.ts @@ -21,6 +21,8 @@ import { logInfo, logSuccess } from './utils/logger' const cookieParser = require('cookie-parser') const healthcheck = require('express-healthcheck') +import { apiWhiteListLogger } from './utils/apiWhiteList' + function haltOnTimedOut(req: Express.Request, _: Express.Response, next: NextFunction) { if (!req.timedout) { next() @@ -37,8 +39,12 @@ export class Server { protected app = express() private keycloak?: CustomKeycloak private constructor() { + const sessionConfig = getSessionConfig() + this.app.use(expressSession(sessionConfig)) + + this.app.all('*', apiWhiteListLogger()) this.setCookie() - this.setKeyCloak() + this.setKeyCloak(sessionConfig) this.authoringProxies() this.configureMiddleware() this.servePublicApi() @@ -116,10 +122,9 @@ export class Server { ) this.app.use(haltOnTimedOut) } - private setKeyCloak() { - const sessionConfig = getSessionConfig() + // tslint:disable-next-line: no-any + private setKeyCloak(sessionConfig: any) { this.keycloak = new CustomKeycloak(sessionConfig) - this.app.use(expressSession(sessionConfig)) this.app.use(this.keycloak.middleware) } diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts new file mode 100644 index 00000000..e8b66d35 --- /dev/null +++ b/src/utils/apiWhiteList.ts @@ -0,0 +1,232 @@ +const _ = require('lodash') +const uuidv1 = require('uuid/v1') +const { pathToRegexp } = require('path-to-regexp') +const dateFormat = require('dateformat') + +import { NextFunction, Request, Response } from 'express' +import { CONSTANTS } from './env' +import { pino } from './fileLogger' +import { logError, logInfo } from './logger' +import { API_LIST } from './whitelistApis' + +/** + * @param { String } REQ_URL - Request URL + * @returns { Boolean } - Return boolean value based on exclude path criteria + */ +// tslint:disable-next-line: no-any +const checkIsStaticRoute = (REQ_URL: any) => { + const excludePath = [ + '.md', + '.js', + '.js.map', + '.ico', + '.ttf', + '.woff2', + '.woff', + '.eot', + '.svg', + '.gif', + '.png', + '.html', + '/dist/', + '/streaming/', + '/resourcebundles/', + '/assets/', + '/content-plugins/', + '/editors/', + ] + // tslint:disable-next-line: no-any + return _.some(excludePath, (path: any) => _.includes(REQ_URL, path)) +} + +const shouldAllow = (req: Request) => { + // Ignore static routes + return req.path === '/' || checkIsStaticRoute(req.path) +} + +/** + * @description + * Set of methods which checks for certain condition on URL + * @since release-3.1.0 + */ +const urlChecks = { + // tslint:disable-next-line: no-any + ROLE_CHECK: (resolve: any, reject: any, req: Request, rolesForURL: any, REQ_URL: any) => { + const data = (_.get(req, 'session.userRoles')) ? _.get(req, 'session.userRoles') : [] + logInfo('Portal_API_WHITELIST : Middleware for URL [ ' + REQ_URL + ' ]') + if (_.includes(rolesForURL, 'ALL') && data.length > 0) { + resolve() + } else if (_.intersection(rolesForURL, data).length > 0) { + resolve() + } else { + return reject('User doesn\'t have appropriate roles') + } + }, +} + +/** + * @param {Object} req - Request Object + * @param {Object} res - Response Object + * @param {Function} next - Function for next middleware + * @param {Array} checksToExecute - Array of methods (checks; defined in urlChecks object) + * @description + * 1. Array of methods executed by promise + * 2. On success; API is allowed + * 3. On error; 403 Forbidden response is sent + * @since release-3.1.0 + */ +// tslint:disable-next-line: no-any +const executeChecks = async (req: Request, res: Response , next: NextFunction, checksToExecute: any = []) => { + try { + // tslint:disable-next-line: no-any + await (Promise as any).allSettled(checksToExecute) + // tslint:disable-next-line: no-any + .then((pSuccess: any) => { + if (pSuccess) { + const _isRejected = _.find(pSuccess, {status: 'rejected'}) + if (_isRejected) { + throw new Error(_isRejected.reason) + } else { + next() + } + } else { + throw new Error('API whitelisting validation failed') + } + }) + // tslint:disable-next-line: no-any + .catch((pError: any) => { + logError(pError) + respond403(req, res) + }) + } catch (error) { + logError(error) + respond403(req, res) + } +} + +/** + * @param {Object} req - Request Object + * @param {Object} res - Response Object + * @description - Generic function to return 403 Forbidden response + * @since release-3.1.0 + */ +const respond403 = (req: Request, res: Response) => { + const REQ_URL = req.path + const err = ({ msg: 'API WHITELIST :: Forbidden access for API [ ' + REQ_URL + ' ]', url: REQ_URL }) + logError(err.msg) + res.status(403) + res.send( + { + id: 'api.error', + ver: '1.0', + // tslint:disable-next-line: object-literal-sort-keys + ts: dateFormat(new Date(), 'yyyy-mm-dd HH:MM:ss:lo'), + params: + { + resmsgid: uuidv1(), + // tslint:disable-next-line: object-literal-sort-keys + msgid: null, + status: 'failed', + err: 'FORBIDDEN_ERROR', + errmsg: 'Forbidden: API WHITELIST Access is denied', + }, + responseCode: 'FORBIDDEN', + result: {}, + }) + res.end() +} + +/** + * @description - Function to check whether + * 1. Incoming API is whitelisted (or) not + * 2. If API does not exists then request is terminated and 403 Forbidden response is sent + * 3. If incoming API is present in API_LIST (whitelistApis); then check URL pattern matching + * 4. Refer `API_LIST` and execute the checks defined + * @since release-3.1.0 + */ +export const isAllowed = () => { + // tslint:disable-next-line: only-arrow-functions + return function(req: Request, res: Response, next: NextFunction) { + if (CONSTANTS.PORTAL_API_WHITELIST_CHECK) { + if (shouldAllow(req)) { + next() + } else { + let REQ_URL = req.path + // Pattern match for URL + // tslint:disable-next-line: no-any + _.forEach(API_LIST.URL_PATTERN, (url: any) => { + const regExp = pathToRegexp(url) + if (regExp.test(REQ_URL)) { + REQ_URL = url + return false + } + return true + }) + // Is API whitelisted ? + if (_.get(API_LIST.URL, REQ_URL)) { + const URL_RULE_OBJ = _.get(API_LIST.URL, REQ_URL) + // tslint:disable-next-line: no-any + const checksToExecute: any[] = [] + + // Iterate for checks defined for API and push to array + if (_.isEmpty(URL_RULE_OBJ.checksNeeded)) { + next() + } else { + const urlChecksNeeded = URL_RULE_OBJ.checksNeeded + // tslint:disable-next-line: no-any + urlChecksNeeded.forEach((CHECK: any) => { + // tslint:disable-next-line: no-shadowed-variable + checksToExecute.push(new Promise((res, rej) => { + if (_.get(URL_RULE_OBJ, CHECK) && typeof urlChecks[CHECK] === 'function') { + urlChecks[CHECK](res, rej, req, URL_RULE_OBJ[CHECK], REQ_URL) + } + })) + }) + executeChecks(req, res, next, checksToExecute) + } + } else { + // If API is not whitelisted + logInfo('Portal_API_WHITELIST: URL not whitelisted') + respond403(req, res) + } + } + } else { + next() + } + } +} +/** + * This function is used for checking whether + */ +export function apiWhiteListLogger() { + return (req: Request, _res: Response, next: NextFunction) => { + if (req.path === '/' || checkIsStaticRoute(req.path)) { + next() + } else { + let REQ_URL = req.path + // Pattern match for URL + // tslint:disable-next-line: no-any + _.forEach(API_LIST.URL_PATTERN, (url: any) => { + const regExp = pathToRegexp(url) + if (regExp.test(REQ_URL)) { + REQ_URL = url + return false + } else { + return true + } + }) + + pino.info('In WhilteList Call========' + REQ_URL) + next() + + // Is API whitelisted ? + // if (_.get(API_LIST.URL, REQ_URL)) { + // next(); + // } else { + // // If API is not whitelisted + // logInfo('Portal_API_WHITELIST_LOGGER: URL not whitelisted'); + // respond403(req, res); + // } + } + } +} diff --git a/src/utils/env.ts b/src/utils/env.ts index a990330d..0b411c97 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -78,6 +78,7 @@ export const CONSTANTS = { DISCUSSION_HUB_WRITE_API_UID: env.DISCUSSION_HUB_WRITE_API_UID || 1, OPEN_SABER_USER_REGISTRY_BASE: env.OPEN_SABER_USER_REGISTRY_BASE || 'http://localhost:8005', PID_API_BASE: env.PID_API_BASE || 'http://localhost:9200', + PORTAL_API_WHITELIST_CHECK: env.PORTAL_API_WHITELIST_CHECK || 'true', PLAYLISTV1_API_BASE: env.PLAYLISTV1_API_BASE || env.SBEXT_API_BASE_2, PLAYLIST_API_BASE: env.PLAYLIST_API_BASE || env.SBEXT_API_BASE, // tslint:disable-next-line:ban @@ -130,7 +131,7 @@ export const CONSTANTS = { DISCUSSION_CATEGORY_LIST: env.DISCUSSION_CATEGORY_LIST || 'cid[]=5&cid[]=6&cid[]=8&cid[]=9&cid[]=10&cid[]=11&cid[]=12&cid[]=13', WORKFLOW_HANDLER_SERVICE_API_BASE: env.WORKFLOW_HANDLER_SERVICE_API_BASE || 'http://localhost:5099', SUNBIRD_PROXY_URL: env.SUNBIRD_PROXY_URL || 'https://igot-sunbird.idc.tarento.com/apis/proxies/v8/action', - SUNBIRD_PROXY_API_BASE: env.SUNBIRD_PROXY_API_BASE || 'https://igot-sunbird.idc.tarento.com/api', + SUNBIRD_PROXY_API_BASE: env.SUNBIRD_PROXY_API_BASE || 'https://igot-dev.in/api', SCORING_SERVICE_API_BASE: env.SCORING_SERVICE_API_BASE || 'http://localhost:7014', // tslint:disable-next-line:max-line-length SB_API_KEY: env.SB_API_KEY || 'bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJRekw4VVA1dUtqUFdaZVpMd1ZtTFJvNHdqWTg2a2FrcSJ9.TPjV0xLacSbp3FbJ7XeqHoKFN35Rl4YHx3DZNN9pm0o', diff --git a/src/utils/fileLogger.ts b/src/utils/fileLogger.ts new file mode 100644 index 00000000..f556a345 --- /dev/null +++ b/src/utils/fileLogger.ts @@ -0,0 +1,21 @@ +'use strict' + +const fs = require('fs') +const fileFormat = () => { + let date = new Date().toISOString().substr(0, 10) + date = date.replace(/:/g, '_') + date = date.replace(/-/g, '_') + date = date.replace(/T/g, '_') + const name = date + '.log' + return process.cwd() + '/logs/' + name +} + +const logFile = fs.createWriteStream(fileFormat(), {flags : 'w'}) +export let pino = require('pino')({}, +{ + [Symbol.for('needsMetadata')]: true, + // tslint:disable-next-line: no-any + write(msg: any) { + logFile.write(msg) + }, +}) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 172aa947..6a492fe0 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -32,6 +32,29 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { // tslint:disable-next-line: no-any proxy.on('proxyRes', (proxyRes: any, req: any, _res: any, ) => { + + // write user session with roles + if (req.originalUrl.includes('/user/v2/read')) { + // tslint:disable-next-line: no-any + proxyRes.on('data', (data: any) => { + if ((proxyRes.statusCode === 200 || proxyRes.statusCode === 201)) { + data = JSON.parse(data.toString('utf-8')) + const roles = data.result.response.roles + req.session.userId = data.result.response.id ? data.result.response.id : data.result.response.userId + req.session.userName = data.result.response.userName + req.session.userRoles = roles + // console.log(req); + // tslint:disable-next-line: only-arrow-functions + req.session.save(function(error: string) { + if (error) { + // tslint:disable-next-line: no-console + console.log(error) + } + }) + } + }) + } + if (req.originalUrl.includes('/discussion/user/v1/create')) { const nodebb_auth_token = proxyRes.headers.nodebb_auth_token if (req.session) { diff --git a/src/utils/requestExtract.ts b/src/utils/requestExtract.ts index 65872b15..01dd0041 100644 --- a/src/utils/requestExtract.ts +++ b/src/utils/requestExtract.ts @@ -45,9 +45,9 @@ export const extractUserToken = (req: IAuthorizedRequest) => { } export const extractAuthorizationFromRequest = (req: IAuthorizedRequest): string => { - const authorization = req.header('Authorization') - - return authorization as string + const token = req.kauth && req.kauth.grant.access_token.token + // Bearer is added as other areas are using split function to get the token + return 'Bearer ' + token } export const extractUserTokenFromRequest = (req: IAuthorizedRequest): string => { const xAuthorization = req.header('X-Authenticated-User-Token') diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts new file mode 100644 index 00000000..c9dff391 --- /dev/null +++ b/src/utils/whitelistApis.ts @@ -0,0 +1,49 @@ +'use strict' +const CHECK = { + ROLE: 'ROLE_CHECK', +} + +// Valid Roles +const ROLE = { + CONTENT_CREATOR: 'CONTENT_CREATOR', + // tslint:disable-next-line: object-literal-sort-keys + COMPENTENCY_REVIEWER: 'competencyReviewer', + EDITOR: 'EDITOR', + IFU_MEMBER: 'IFUMember', + MDO_ADMIN: 'MDO ADMIN', + MEMBER: 'Member', + PUBLISHER: 'PUBLISHER', + REVIEWER: 'REVIEWER', + FRAC_ACCESS_COMPENTENCY: 'fracAccessCompetency', + FRAC_ADMIN: 'fracAdmin', + FRAC_REVIEWER_ONE: 'fracReviewerOne', + FRAC_REVIEWER_TWO: 'fracReviewerTwo', + PUBLIC: 'PUBLIC', +} + +// All api list validations +export const API_LIST = { + URL: + { + '/protected/v8/user/profileDetails/test': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ROLE.CONTENT_CREATOR, + ], + }, + '/proxies/v8/api/user/v2/read': { + checksNeeded: [], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + }, + URL_PATTERN: + [ + '/proxies/v8/api/user/v2/read', + '/protected/v8/user/profileDetails/test', + ], +} From 3b57772174fcfa381edcd331055b6fcebfa5a1ea Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Tue, 29 Jun 2021 19:29:59 +0530 Subject: [PATCH 002/856] Changes for logs --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 2872abdf..4c8ab73e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,6 +2,7 @@ FROM node:12 WORKDIR /usr/src/app RUN mkdir -p /usr/src/app/user_upload +RUN mkdir -p /usr/src/app/logs COPY package*.json ./ RUN npm install --only=production From bbd0779daa6805fa718bc0f97501a3f9032fdd88 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Sat, 10 Jul 2021 14:16:01 +0530 Subject: [PATCH 003/856] Release - Changes for Keycloak n rbac --- package-lock.json | 5 ++ package.json | 1 + src/configs/keycloak.config.ts | 6 ++- src/protectedApi_v8/user/resource.ts | 6 +++ src/protectedApi_v8/user/user.ts | 24 +++++----- src/server.ts | 1 - src/utils/apiWhiteList.ts | 70 ++++++++++++++++++---------- src/utils/custom-keycloak.ts | 39 ++++++++++++++-- src/utils/env.ts | 5 +- src/utils/permissionHelper.ts | 45 ++++++++++++++++++ src/utils/whitelistApis.ts | 24 ++++++++++ 11 files changed, 180 insertions(+), 46 deletions(-) create mode 100644 src/protectedApi_v8/user/resource.ts create mode 100644 src/utils/permissionHelper.ts diff --git a/package-lock.json b/package-lock.json index 3060d82f..8e5506df 100644 --- a/package-lock.json +++ b/package-lock.json @@ -682,6 +682,11 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, + "async": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", + "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" + }, "async-done": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.1.tgz", diff --git a/package.json b/package.json index 911f361f..f2e6b44b 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "dependencies": { "@types/cors": "^2.8.6", "@types/node-xlsx": "^0.15.0", + "async": "^3.2.0", "axios": "^0.19.1", "cassandra-driver": "^4.1.0", "cassandra-store": "^5.0.0", diff --git a/src/configs/keycloak.config.ts b/src/configs/keycloak.config.ts index 61244877..3a8f8751 100644 --- a/src/configs/keycloak.config.ts +++ b/src/configs/keycloak.config.ts @@ -2,9 +2,11 @@ import { CONSTANTS } from '../utils/env' export function getKeycloakConfig(url?: string, realm?: string) { return { - bearerOnly: true, + 'ssl-required': 'none', + // tslint:disable-next-line: object-literal-sort-keys + 'public-client': true, realm: realm ? realm : CONSTANTS.KEYCLOAK_REALM, resource: 'portal', - serverUrl: url ? `${url}` : `${CONSTANTS.HTTPS_HOST}/auth`, + 'auth-server-url': url ? `${url}` : `${CONSTANTS.HTTPS_HOST}/auth`, } } diff --git a/src/protectedApi_v8/user/resource.ts b/src/protectedApi_v8/user/resource.ts new file mode 100644 index 00000000..c63f65c2 --- /dev/null +++ b/src/protectedApi_v8/user/resource.ts @@ -0,0 +1,6 @@ +import { Router } from 'express' +export const userAuthKeyCloakApi = Router() +userAuthKeyCloakApi.get('/', async (req, res) => { + res.redirect(`https://${req.get('host')}/`) // Prod setup + // res.redirect('http://localhost:3000/') // Test setup +}) diff --git a/src/protectedApi_v8/user/user.ts b/src/protectedApi_v8/user/user.ts index 5af72bd6..12bca56c 100644 --- a/src/protectedApi_v8/user/user.ts +++ b/src/protectedApi_v8/user/user.ts @@ -37,6 +37,7 @@ import { progressApi } from './progress' import { ratingApi } from './rating' import { rdbmsApi } from './rdbms' import { realTimeProgressApi } from './realTimeProgress' +import { userAuthKeyCloakApi } from './resource' import { rolesApi } from './roles' import { shareApi } from './share' import { skillsApi } from './skills' @@ -56,13 +57,13 @@ user.use('/account-settings', accountSettingsApi) user.use('/mini-profile', userMiniProfile) user.use('/activity', activity) user.use('/change-email', changeEmailApi) -user.use('/autocomplete', autocompleteApi) +user.use('/autocomplete', autocompleteApi) // Validate user.use('/badge', badgeApi) user.use('/class-diagram', classDiagramApi) user.use('/code', codeApi) -user.use('/content', userContentApi) +user.use('/content', userContentApi) // Validate user.use('/dashboard', dashboardApi) -user.use('/details', detailsApi) +user.use('/details', detailsApi) // Validate user.use('/email', emailApi) user.use('/emailToUserId', emailToUserIdApi) user.use('/evaluate', evaluateApi) @@ -70,24 +71,25 @@ user.use('/feedback', feedbackApi) user.use('/feedbackV2', feedbackV2Api) user.use('/follow', followApi) user.use('/goals', goalsApi) -user.use('/history', historyApi) +user.use('/history', historyApi) // Validate user.use('/iconBadge', iconBadgeApi) user.use('/myAnalytics', myAnalyticsApi) -user.use('/notifications', notificationsApi) +user.use('/notifications', notificationsApi) // Validate user.use('/ocm', ocmApi) user.use('/playlist', playlistApi) -user.use('/preference', protectedPreference) +user.use('/preference', protectedPreference) // Validate user.use('/profile', profileApi) -user.use('/profileDetails', profileDeatailsApi) +user.use('/profileDetails', profileDeatailsApi) // Validate user.use('/progress', progressApi) -user.use('/rating', ratingApi) +user.use('/rating', ratingApi) // Validate user.use('/rdbms', rdbmsApi) user.use('/roles', rolesApi) user.use('/share', shareApi) user.use('/skills', skillsApi) -user.use('/telemetry', telemetryApi) +user.use('/telemetry', telemetryApi) // Validate user.use('/tnc', protectedTnc) user.use('/token', userTokenApi) +user.use('/resource', userAuthKeyCloakApi) user.use('/topic', topicApi) user.use('/topics', topicsApi) user.use('/viewprofile', viewProfileApi) @@ -95,5 +97,5 @@ user.use('/validate', validateApi) user.use('/realTimeProgress', realTimeProgressApi) user.use('/exercise', exerciseApi) user.use('/users', usersApi) -user.use('/mandatoryContent', mandatoryContent) -user.use('/profileRegistry', profileRegistryApi) +user.use('/mandatoryContent', mandatoryContent) // Validate +user.use('/profileRegistry', profileRegistryApi) // Validate diff --git a/src/server.ts b/src/server.ts index 7a03d3ae..ecfbc19f 100644 --- a/src/server.ts +++ b/src/server.ts @@ -41,7 +41,6 @@ export class Server { private constructor() { const sessionConfig = getSessionConfig() this.app.use(expressSession(sessionConfig)) - this.app.all('*', apiWhiteListLogger()) this.setCookie() this.setKeyCloak(sessionConfig) diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index e8b66d35..a716dd69 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -5,7 +5,6 @@ const dateFormat = require('dateformat') import { NextFunction, Request, Response } from 'express' import { CONSTANTS } from './env' -import { pino } from './fileLogger' import { logError, logInfo } from './logger' import { API_LIST } from './whitelistApis' @@ -136,6 +135,33 @@ const respond403 = (req: Request, res: Response) => { res.end() } +const respond419 = (req: Request, res: Response) => { + const REQ_URL = req.path + const err = ({ msg: 'API WHITELIST :: Unauthorized access for API [ ' + REQ_URL + ' ]', url: REQ_URL }) + logError(err.msg) + res.status(419) + res.send( + { + id: 'api.error', + ver: '1.0', + // tslint:disable-next-line: object-literal-sort-keys + ts: dateFormat(new Date(), 'yyyy-mm-dd HH:MM:ss:lo'), + params: + { + resmsgid: uuidv1(), + // tslint:disable-next-line: object-literal-sort-keys + msgid: null, + status: 'failed', + err: 'UNAUTHORIZED_ERROR', + errmsg: 'UNAUTHORIZED: Access is denied', + }, + responseCode: 'UNAUTHORIZED', + redirectUrl: redirectToLogin(req), + result: {}, + }) + res.end() +} + /** * @description - Function to check whether * 1. Incoming API is whitelisted (or) not @@ -195,38 +221,32 @@ export const isAllowed = () => { } } } +const redirectToLogin = (req: Request) => { + const redirectUrl = 'apis/protected/v8/user/resource' + return `https://${req.get('host')}/${redirectUrl}` // 'http://localhost:3003/protected/v8/user/resource/' +} /** * This function is used for checking whether */ export function apiWhiteListLogger() { - return (req: Request, _res: Response, next: NextFunction) => { + return (req: Request, res: Response, next: NextFunction) => { if (req.path === '/' || checkIsStaticRoute(req.path)) { next() + return + } + const REQ_URL = req.path + if (!_.includes(REQ_URL, '/resource') && (req.session)) { + if (!('userRoles' in req.session) || (('userRoles' in req.session) && (req.session.userRoles.length === 0))) { + // console.log('Session not there: In If') + logError('Portal_API_WHITELIST_LOGGER: User needs to authenticated themselves') + respond419(req, res) + } else { + // Pattern match for URL + next() + logInfo('In WhilteList Call========' + REQ_URL) + } } else { - let REQ_URL = req.path - // Pattern match for URL - // tslint:disable-next-line: no-any - _.forEach(API_LIST.URL_PATTERN, (url: any) => { - const regExp = pathToRegexp(url) - if (regExp.test(REQ_URL)) { - REQ_URL = url - return false - } else { - return true - } - }) - - pino.info('In WhilteList Call========' + REQ_URL) next() - - // Is API whitelisted ? - // if (_.get(API_LIST.URL, REQ_URL)) { - // next(); - // } else { - // // If API is not whitelisted - // logInfo('Portal_API_WHITELIST_LOGGER: URL not whitelisted'); - // respond403(req, res); - // } } } } diff --git a/src/utils/custom-keycloak.ts b/src/utils/custom-keycloak.ts index 540bb01a..cfa689fb 100644 --- a/src/utils/custom-keycloak.ts +++ b/src/utils/custom-keycloak.ts @@ -3,7 +3,9 @@ import expressSession from 'express-session' import keycloakConnect from 'keycloak-connect' import { getKeycloakConfig } from '../configs/keycloak.config' import { CONSTANTS } from './env' -import { logInfo } from './logger' +import { logError, logInfo } from './logger' +import { PERMISSION_HELPER } from './permissionHelper' +const async = require('async') const composable = require('composable-middleware') @@ -28,7 +30,7 @@ export class CustomKeycloak { const middleware = composable( keycloak.middleware({ admin: '/callback', - logout: '/logout', + logout: '/reset', }) ) middleware(req, res, next) @@ -50,10 +52,37 @@ export class CustomKeycloak { this.multiTenantKeycloak.get('common')) as keycloakConnect } - authenticated = () => { - logInfo(`${process.pid}: User authenticated`) + // tslint:disable-next-line: no-any + authenticated = async (request: any) => { + // console.log('Step 3: authenticated function') + try { + const userId = request.kauth.grant.access_token.content.sub.split(':') + request.session.userId = userId[userId.length - 1] + } catch (err) { + logError('userId conversation error' + request.kauth.grant.access_token.content.sub) + } + const postLoginRequest = [] + // tslint:disable-next-line: no-any + postLoginRequest.push((callback: any) => { + // console.log('in pus') + PERMISSION_HELPER.getCurrentUserRoles(request, callback) + }) + + // tslint:disable-next-line: no-any + async.series(postLoginRequest, (err: any) => { + if (err) { + logError('error loggin in user') + } else { + logInfo(`${process.pid}: User authenticated`) + } + }) } - deauthenticated = () => { + + // tslint:disable-next-line: no-any + deauthenticated = (request: any) => { + // console.log('De') + delete request.session.userRoles + delete request.session.userId logInfo(`${process.pid}: User Deauthenticated`) } diff --git a/src/utils/env.ts b/src/utils/env.ts index 0b411c97..ab7d7f2f 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -1,5 +1,5 @@ const env = process.env -const HTTPS_HOST = env.HTTPS_HOST || 'http://localhost' +const HTTPS_HOST = env.HTTPS_HOST || 'https://igot-dev.in' export const CONSTANTS = { ACCESS_CONTROL_API_BASE: env.ACCESS_CONTROL_API_BASE || env.SBEXT_API_BASE, ANALYTICS_TIMEOUT: env.ANALYTICS_TIMEOUT || 10000, @@ -62,7 +62,7 @@ export const CONSTANTS = { LIKE_API_BASE: env.LIKE_API_BASE || env.SB_EXT_API_BASE_2, MULTI_TENANT_KEYCLOAK: env.MULTI_TENANT_KEYCLOAK || - 'igot;https://igot-sunbird.idc.tarento.com/auth;sunbird', + 'igot,https://igot-dev.in/auth,sunbird', NAVIGATOR_JSON_HOST: env.NAVIGATOR_JSON_HOST || 'http://localhost:3007/web-hosted/navigator/json', NODE_API_BASE: env.NODE_API_BASE || 'http://localhost:5001', @@ -79,6 +79,7 @@ export const CONSTANTS = { OPEN_SABER_USER_REGISTRY_BASE: env.OPEN_SABER_USER_REGISTRY_BASE || 'http://localhost:8005', PID_API_BASE: env.PID_API_BASE || 'http://localhost:9200', PORTAL_API_WHITELIST_CHECK: env.PORTAL_API_WHITELIST_CHECK || 'true', + PORTAL_REALM: env.PORTAL_REALM || 'sunbird', PLAYLISTV1_API_BASE: env.PLAYLISTV1_API_BASE || env.SBEXT_API_BASE_2, PLAYLIST_API_BASE: env.PLAYLIST_API_BASE || env.SBEXT_API_BASE, // tslint:disable-next-line:ban diff --git a/src/utils/permissionHelper.ts b/src/utils/permissionHelper.ts new file mode 100644 index 00000000..a76af67d --- /dev/null +++ b/src/utils/permissionHelper.ts @@ -0,0 +1,45 @@ +import request from 'request' +import { CONSTANTS } from './env' + +export const PERMISSION_HELPER = { + // tslint:disable-next-line: no-any + setRolesData(reqObj: any, callback: any, body: any) { + // tslint:disable-next-line: no-any + const userData: any = JSON.parse(body) + if (reqObj.session) { + reqObj.session.userId = userData.result.response.id ? userData.result.response.id : userData.result.response.userId + reqObj.session.userName = userData.result.response.userName + reqObj.session.userRoles = userData.result.response.roles + // tslint:disable-next-line: no-any + reqObj.session.save((error: any) => { + if (error) { + callback(error, null) + } else { + callback(null, userData) + } + }) + } + }, + // tslint:disable-next-line: no-any + getCurrentUserRoles(reqObj: any, callback: any) { + // console.log('Step 3: Get user roles function') + const userId = reqObj.session.userId + // console.log(userId) + const readUrl = `${CONSTANTS.SUNBIRD_PROXY_API_BASE}/user/v2/read/` + userId + const options = { + headers: { + Authorization: CONSTANTS.SB_API_KEY, + 'X-Channel-Id': CONSTANTS.X_Channel_Id, + 'x-authenticated-user-token': reqObj.kauth.grant.access_token.token, + 'x-authenticated-userid': userId, + }, + url: readUrl, + } + // tslint:disable-next-line: no-any + request.get(options, (_err: any, _httpResponse: any, body: any) => { + if (body) { + this.setRolesData(reqObj, callback, body) + } + }) + }, +} diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index c9dff391..cd8d06f2 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -25,6 +25,13 @@ const ROLE = { export const API_LIST = { URL: { + '/protected/v8/user/details': { + checksNeeded: [], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/user/profileDetails/test': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -33,6 +40,13 @@ export const API_LIST = { ROLE.CONTENT_CREATOR, ], }, + '/protected/v8/user/resource/': { + checksNeeded: [], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/api/user/v2/read': { checksNeeded: [], // tslint:disable-next-line: object-literal-sort-keys @@ -40,10 +54,20 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/reset': { + checksNeeded: [], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ '/proxies/v8/api/user/v2/read', '/protected/v8/user/profileDetails/test', + '/protected/v8/user/resource/', + '/protected/v8/user/details', + '/reset', ], } From 4eb0893cdb728c922db2d529dd666916f7aa7676 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Sun, 11 Jul 2021 19:17:41 +0530 Subject: [PATCH 004/856] setheader --- src/utils/apiWhiteList.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index a716dd69..96e0a790 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -140,6 +140,7 @@ const respond419 = (req: Request, res: Response) => { const err = ({ msg: 'API WHITELIST :: Unauthorized access for API [ ' + REQ_URL + ' ]', url: REQ_URL }) logError(err.msg) res.status(419) + res.setHeader('location', redirectToLogin(req)) res.send( { id: 'api.error', From ed49cda24c40dbdbbf20e2a022eded556f2fa6e4 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Mon, 12 Jul 2021 19:25:57 +0530 Subject: [PATCH 005/856] Changes for resource end point --- src/protectedApi_v8/protectedApiV8.ts | 2 ++ src/protectedApi_v8/{user => }/resource.ts | 0 src/protectedApi_v8/user/user.ts | 2 -- src/utils/apiWhiteList.ts | 2 +- src/utils/whitelistApis.ts | 5 +++-- 5 files changed, 6 insertions(+), 5 deletions(-) rename src/protectedApi_v8/{user => }/resource.ts (100%) diff --git a/src/protectedApi_v8/protectedApiV8.ts b/src/protectedApi_v8/protectedApiV8.ts index 1df3190d..e234272d 100644 --- a/src/protectedApi_v8/protectedApiV8.ts +++ b/src/protectedApi_v8/protectedApiV8.ts @@ -23,6 +23,7 @@ import { networkConnectionApi } from './network' import { networkHubApi } from './network-hub' import { portalApi } from './portal-v3' import { recommendationApi } from './recommendation' +import { userAuthKeyCloakApi } from './resource' import { roleActivityApi } from './roleActivity' import { scoringApi } from './scoring' import { scromApi } from './scrom' @@ -72,5 +73,6 @@ protectedApiV8.use('/portal', portalApi) protectedApiV8.use('/scroing', scoringApi) protectedApiV8.use('/workflowhandler', workflowHandlerApi) protectedApiV8.use('/roleactivity', roleActivityApi) +protectedApiV8.use('/resource', userAuthKeyCloakApi) protectedApiV8.use('/workallocation', workAllocationApi) protectedApiV8.use('/frac', fracApi) diff --git a/src/protectedApi_v8/user/resource.ts b/src/protectedApi_v8/resource.ts similarity index 100% rename from src/protectedApi_v8/user/resource.ts rename to src/protectedApi_v8/resource.ts diff --git a/src/protectedApi_v8/user/user.ts b/src/protectedApi_v8/user/user.ts index 12bca56c..61bc1397 100644 --- a/src/protectedApi_v8/user/user.ts +++ b/src/protectedApi_v8/user/user.ts @@ -37,7 +37,6 @@ import { progressApi } from './progress' import { ratingApi } from './rating' import { rdbmsApi } from './rdbms' import { realTimeProgressApi } from './realTimeProgress' -import { userAuthKeyCloakApi } from './resource' import { rolesApi } from './roles' import { shareApi } from './share' import { skillsApi } from './skills' @@ -89,7 +88,6 @@ user.use('/skills', skillsApi) user.use('/telemetry', telemetryApi) // Validate user.use('/tnc', protectedTnc) user.use('/token', userTokenApi) -user.use('/resource', userAuthKeyCloakApi) user.use('/topic', topicApi) user.use('/topics', topicsApi) user.use('/viewprofile', viewProfileApi) diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index 96e0a790..579fd658 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -223,7 +223,7 @@ export const isAllowed = () => { } } const redirectToLogin = (req: Request) => { - const redirectUrl = 'apis/protected/v8/user/resource' + const redirectUrl = '/protected/v8/resource/' return `https://${req.get('host')}/${redirectUrl}` // 'http://localhost:3003/protected/v8/user/resource/' } /** diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index cd8d06f2..e526b14f 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -40,7 +40,8 @@ export const API_LIST = { ROLE.CONTENT_CREATOR, ], }, - '/protected/v8/user/resource/': { + // tslint:disable-next-line: object-literal-sort-keys + '/protected/v8/resource/': { checksNeeded: [], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -66,7 +67,7 @@ export const API_LIST = { [ '/proxies/v8/api/user/v2/read', '/protected/v8/user/profileDetails/test', - '/protected/v8/user/resource/', + '/protected/v8/resource/', '/protected/v8/user/details', '/reset', ], From 6d99e0669efed4e09cad6da831cf17a4679530d9 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Mon, 12 Jul 2021 20:06:24 +0530 Subject: [PATCH 006/856] fix --- src/utils/apiWhiteList.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index 579fd658..72ffe17f 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -223,7 +223,7 @@ export const isAllowed = () => { } } const redirectToLogin = (req: Request) => { - const redirectUrl = '/protected/v8/resource/' + const redirectUrl = 'protected/v8/resource/' return `https://${req.get('host')}/${redirectUrl}` // 'http://localhost:3003/protected/v8/user/resource/' } /** From 1a8ce3f227babd6805909925ace7f5cee61f091b Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Wed, 14 Jul 2021 16:58:02 +0530 Subject: [PATCH 007/856] Task54765- Changes for redirect --- src/protectedApi_v8/resource.ts | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/protectedApi_v8/resource.ts b/src/protectedApi_v8/resource.ts index c63f65c2..ade9b643 100644 --- a/src/protectedApi_v8/resource.ts +++ b/src/protectedApi_v8/resource.ts @@ -1,6 +1,27 @@ import { Router } from 'express' export const userAuthKeyCloakApi = Router() userAuthKeyCloakApi.get('/', async (req, res) => { - res.redirect(`https://${req.get('host')}/`) // Prod setup - // res.redirect('http://localhost:3000/') // Test setup + const host = req.get('host') + let redirectUrl = '' + switch (host) { + case 'igot-dev.in': + redirectUrl = 'https://' + host + '/page/home' + break + case 'mdo.igot-dev.in': + redirectUrl = 'https://' + host + '/app/home/welcome' + break + case 'spv.igot-dev.in': + redirectUrl = 'https://' + host + '/app/home/directory' + break + case 'cbc.igot-dev.in': + redirectUrl = 'https://' + host + '/app/home/welcome' + break + case 'cbp.igot-dev.in': + redirectUrl = 'https://' + host + '/author/cbp/me' + break + default: + redirectUrl = 'http://localhost:3000/app/home/directory' // Test setup + break + } + res.redirect(redirectUrl) }) From 875a0eaf3bb06c51b38a17b23dd80c7e0ee48d57 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Wed, 14 Jul 2021 22:06:46 +0530 Subject: [PATCH 008/856] Task54765 - Changes for keycloak --- src/configs/keycloak.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/configs/keycloak.config.ts b/src/configs/keycloak.config.ts index 3a8f8751..0956df05 100644 --- a/src/configs/keycloak.config.ts +++ b/src/configs/keycloak.config.ts @@ -2,7 +2,7 @@ import { CONSTANTS } from '../utils/env' export function getKeycloakConfig(url?: string, realm?: string) { return { - 'ssl-required': 'none', + 'ssl-required': 'external', // tslint:disable-next-line: object-literal-sort-keys 'public-client': true, realm: realm ? realm : CONSTANTS.KEYCLOAK_REALM, From 6f228edba89eb5806ad55cbd5490fb9754a91554 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Fri, 16 Jul 2021 22:56:01 +0530 Subject: [PATCH 009/856] Task54765 - Changes for local redirect --- src/protectedApi_v8/resource.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/protectedApi_v8/resource.ts b/src/protectedApi_v8/resource.ts index ade9b643..82794023 100644 --- a/src/protectedApi_v8/resource.ts +++ b/src/protectedApi_v8/resource.ts @@ -1,7 +1,12 @@ import { Router } from 'express' export const userAuthKeyCloakApi = Router() userAuthKeyCloakApi.get('/', async (req, res) => { - const host = req.get('host') + let host = req.get('host') + const queryParam = req.query.q + if (queryParam.includes('localhost')) { + host = queryParam + } + let redirectUrl = '' switch (host) { case 'igot-dev.in': @@ -20,7 +25,7 @@ userAuthKeyCloakApi.get('/', async (req, res) => { redirectUrl = 'https://' + host + '/author/cbp/me' break default: - redirectUrl = 'http://localhost:3000/app/home/directory' // Test setup + redirectUrl = queryParam // local setup break } res.redirect(redirectUrl) From 4d0328c0345e3fcc669152f99b52801ce005c7c2 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Fri, 16 Jul 2021 23:06:17 +0530 Subject: [PATCH 010/856] Task54765- Added check for query param --- src/protectedApi_v8/resource.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/protectedApi_v8/resource.ts b/src/protectedApi_v8/resource.ts index 82794023..c176a98d 100644 --- a/src/protectedApi_v8/resource.ts +++ b/src/protectedApi_v8/resource.ts @@ -1,12 +1,15 @@ import { Router } from 'express' +const _ = require('lodash') export const userAuthKeyCloakApi = Router() userAuthKeyCloakApi.get('/', async (req, res) => { let host = req.get('host') - const queryParam = req.query.q - if (queryParam.includes('localhost')) { - host = queryParam + let queryParam = '' + if (!_.isEmpty(req.query)) { + queryParam = req.query.q + if (queryParam.includes('localhost')) { + host = queryParam + } } - let redirectUrl = '' switch (host) { case 'igot-dev.in': From 0d1d3d26bd0fe9fc57270cf73f29ddbfc17c768e Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Sat, 17 Jul 2021 08:34:14 +0530 Subject: [PATCH 011/856] Task54765-Changes for redirect --- src/protectedApi_v8/resource.ts | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/src/protectedApi_v8/resource.ts b/src/protectedApi_v8/resource.ts index c176a98d..10fbfaf0 100644 --- a/src/protectedApi_v8/resource.ts +++ b/src/protectedApi_v8/resource.ts @@ -2,34 +2,20 @@ import { Router } from 'express' const _ = require('lodash') export const userAuthKeyCloakApi = Router() userAuthKeyCloakApi.get('/', async (req, res) => { - let host = req.get('host') + const host = req.get('host') let queryParam = '' + let isLocal = 0 if (!_.isEmpty(req.query)) { queryParam = req.query.q if (queryParam.includes('localhost')) { - host = queryParam + isLocal = 1 } } let redirectUrl = '' - switch (host) { - case 'igot-dev.in': - redirectUrl = 'https://' + host + '/page/home' - break - case 'mdo.igot-dev.in': - redirectUrl = 'https://' + host + '/app/home/welcome' - break - case 'spv.igot-dev.in': - redirectUrl = 'https://' + host + '/app/home/directory' - break - case 'cbc.igot-dev.in': - redirectUrl = 'https://' + host + '/app/home/welcome' - break - case 'cbp.igot-dev.in': - redirectUrl = 'https://' + host + '/author/cbp/me' - break - default: - redirectUrl = queryParam // local setup - break + if (isLocal) { + redirectUrl = queryParam + } else { + redirectUrl = `https://${host}${queryParam}` // 'https://' + host + '/page/home' } res.redirect(redirectUrl) }) From 3a1be1d7d49d8b0e36ee3cf482c8ba1967cd3ebc Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Tue, 20 Jul 2021 13:17:13 +0530 Subject: [PATCH 012/856] Changes for Cors --- src/server.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/server.ts b/src/server.ts index ecfbc19f..e38b9c5f 100644 --- a/src/server.ts +++ b/src/server.ts @@ -77,7 +77,8 @@ export class Server { this.app.use(express.urlencoded({ extended: false, limit: '50mb' })) this.app.use(express.json({ limit: '50mb' })) this.app.use(fileUpload()) - this.app.use(cors()) + // this.app.use(cors()) + app.use(cors({origin: 'http://local.igot-dev.in:3000', credentials: true})) this.app.use('/healthcheck', healthcheck({ healthy() { return { everything: 'is ok' } From f74ea62b9982409923f121f93a414a959ade20df Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Tue, 20 Jul 2021 13:19:00 +0530 Subject: [PATCH 013/856] cors fix --- src/server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server.ts b/src/server.ts index e38b9c5f..e876008c 100644 --- a/src/server.ts +++ b/src/server.ts @@ -78,7 +78,7 @@ export class Server { this.app.use(express.json({ limit: '50mb' })) this.app.use(fileUpload()) // this.app.use(cors()) - app.use(cors({origin: 'http://local.igot-dev.in:3000', credentials: true})) + this.app.use(cors({origin: 'http://local.igot-dev.in:3000', credentials: true})) this.app.use('/healthcheck', healthcheck({ healthy() { return { everything: 'is ok' } From 6d5bf5078e16958c67bc45120b17f34f206bcf61 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 8 Jul 2021 12:14:29 +0530 Subject: [PATCH 014/856] Handling multiple IP for cassandra --- src/configs/cassandra.config.ts | 6 +++++- src/configs/session.config.ts | 7 ++++++- src/utils/keycloak-user-creation.ts | 6 +++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/configs/cassandra.config.ts b/src/configs/cassandra.config.ts index 1d751e30..f2cb7966 100644 --- a/src/configs/cassandra.config.ts +++ b/src/configs/cassandra.config.ts @@ -2,10 +2,14 @@ import cassandraDriver from 'cassandra-driver' import { CONSTANTS } from '../utils/env' export const cassandraClientOptions: cassandraDriver.ClientOptions = { - contactPoints: [CONSTANTS.CASSANDRA_IP], + contactPoints: getIPList(), keyspace: CONSTANTS.CASSANDRA_KEYSPACE, localDataCenter: 'datacenter1', queryOptions: { prepare: true, }, } + +function getIPList() { + return CONSTANTS.CASSANDRA_IP.split(',') +} diff --git a/src/configs/session.config.ts b/src/configs/session.config.ts index 0ef3a7b2..55589aea 100644 --- a/src/configs/session.config.ts +++ b/src/configs/session.config.ts @@ -6,12 +6,17 @@ import { CONSTANTS } from '../utils/env' let sessionConfig: expressSession.SessionOptions const cassandraClientOptions: cassandraDriver.ClientOptions = { - contactPoints: [CONSTANTS.CASSANDRA_IP], + contactPoints: getIPList(), keyspace: 'portal', queryOptions: { prepare: true, }, } + +function getIPList() { + return CONSTANTS.CASSANDRA_IP.split(',') +} + if ( CONSTANTS.IS_CASSANDRA_AUTH_ENABLED && CONSTANTS.CASSANDRA_USERNAME && diff --git a/src/utils/keycloak-user-creation.ts b/src/utils/keycloak-user-creation.ts index 783187a4..27963bc0 100644 --- a/src/utils/keycloak-user-creation.ts +++ b/src/utils/keycloak-user-creation.ts @@ -9,7 +9,7 @@ const CASSANDRA_KEYSPACE = CONSTANTS.CASSANDRA_KEYSPACE const defaultNewUserPassword = CONSTANTS.KC_NEW_USER_DEFAULT_PWD const cassandraClientOptions: cassandraDriver.ClientOptions = { - contactPoints: [CONSTANTS.CASSANDRA_IP], + contactPoints: getIPList(), keyspace: CASSANDRA_KEYSPACE, localDataCenter: 'datacenter1', queryOptions: { @@ -17,6 +17,10 @@ const cassandraClientOptions: cassandraDriver.ClientOptions = { }, } +function getIPList() { + return CONSTANTS.CASSANDRA_IP.split(',') +} + const keycloakConfig = { baseUrl: `${CONSTANTS.HTTPS_HOST}/auth`, realmName: CONSTANTS.KEYCLOAK_REALM, From 7be458144802d36714b28e22d31445182e825574 Mon Sep 17 00:00:00 2001 From: gohilamariappan <41056032+gohilamariappan@users.noreply.github.com> Date: Mon, 12 Jul 2021 13:02:58 +0530 Subject: [PATCH 015/856] docker server repo variable updated --- Jenkinsfile-sun | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile-sun b/Jenkinsfile-sun index 467274d8..3e653d5c 100644 --- a/Jenkinsfile-sun +++ b/Jenkinsfile-sun @@ -38,7 +38,7 @@ node('build-slave') { env.NODE_ENV = "build" print "Environment will be : ${env.NODE_ENV}" sh('chmod 777 build.sh') - sh("bash -x build.sh ${build_tag} ${env.NODE_NAME} ${hub_org}") + sh("bash -x build.sh ${build_tag} ${env.NODE_NAME} ${docker_server}") } stage('ArchiveArtifacts') { From 527e51e0135b0075f47a06b9d1a38af1eb1aaf74 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Wed, 14 Jul 2021 12:54:52 +0530 Subject: [PATCH 016/856] Added kong routes to content-validation and scoring engine --- src/protectedApi_v8/contentValidation.ts | 28 +++++++++++++++++++----- src/protectedApi_v8/scoring.ts | 16 +++++++++++--- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/protectedApi_v8/contentValidation.ts b/src/protectedApi_v8/contentValidation.ts index a39d0ec1..fc8916b2 100644 --- a/src/protectedApi_v8/contentValidation.ts +++ b/src/protectedApi_v8/contentValidation.ts @@ -5,16 +5,17 @@ import { axiosRequestConfig } from '../configs/request.config' import { CONSTANTS } from '../utils/env' import { logError } from '../utils/logger' import { ERROR } from '../utils/message' +import { extractUserToken } from '../utils/requestExtract' const API_END_POINTS = { checkPdfProfanity: `${CONSTANTS.CONTENT_VALIDATION_API_BASE}/contentValidation/v1/checkPdfProfanity`, checkProfanity: (contentId: string, userId: string) => `${CONSTANTS.CONTENT_VALIDATION_API_BASE}/contentValidation/v1/checkProfanity/${contentId}/${userId}`, checkTextProfanity: `${CONSTANTS.PROFANITY_SERVICE_API_BASE}/checkProfanity`, - getPdfProfanity: `${CONSTANTS.CONTENT_VALIDATION_API_BASE}/contentValidation/v1/getPdfProfanity`, + getPdfProfanity: `${CONSTANTS.KONG_API_BASE}/contentValidation/v1/getPdfProfanity`, getPdfProfanityForContent: (contentId: string) => - `${CONSTANTS.CONTENT_VALIDATION_API_BASE}/contentValidation/v1/getPdfProfanityForContent/${contentId}`, - startPdfProfanity: `${CONSTANTS.CONTENT_VALIDATION_API_BASE}/contentValidation/v1/startPdfProfanity`, + `${CONSTANTS.KONG_API_BASE}/contentValidation/v1/getPdfProfanityForContent/${contentId}`, + startPdfProfanity: `${CONSTANTS.KONG_API_BASE}/contentValidation/v1/startPdfProfanity`, } export const contentValidationApi = Router() @@ -82,7 +83,14 @@ contentValidationApi.post('/validatePdfContent', async (req, res) => { contentValidationApi.post('/startPdfProfanity', async (req, res) => { try { - const response = await axios.post(API_END_POINTS.startPdfProfanity, req.body, axiosRequestConfig) + const response = await axios.post(API_END_POINTS.startPdfProfanity, req.body, { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), + }, + }) res.status(response.status).send(response.data) } catch (err) { logError(failedToProcess + err) @@ -96,7 +104,14 @@ contentValidationApi.post('/startPdfProfanity', async (req, res) => { contentValidationApi.post('/getPdfProfanity', async (req, res) => { try { - const response = await axios.post(API_END_POINTS.getPdfProfanity, req.body, axiosRequestConfig) + const response = await axios.post(API_END_POINTS.getPdfProfanity, req.body, { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), + }, + }) res.status(response.status).send(response.data) } catch (err) { logError(failedToProcess + err) @@ -116,9 +131,12 @@ contentValidationApi.get('/getPdfProfanityForContent/:contentId', async (req, re const response = await axios.get(API_END_POINTS.getPdfProfanityForContent(contentId), { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, 'Content-Type': 'application/json', rootOrg: rootOrgValue, wid, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, }) res.status(response.status).send(response.data) diff --git a/src/protectedApi_v8/scoring.ts b/src/protectedApi_v8/scoring.ts index 8b472751..5cf6270f 100644 --- a/src/protectedApi_v8/scoring.ts +++ b/src/protectedApi_v8/scoring.ts @@ -4,11 +4,12 @@ import { axiosRequestConfig } from '../configs/request.config' import { CONSTANTS } from '../utils/env' import { logError } from '../utils/logger' import { ERROR } from '../utils/message' +import { extractUserToken } from '../utils/requestExtract' const API_END_POINTS = { - calculateScoreEndPoint: `${CONSTANTS.SCORING_SERVICE_API_BASE}/action/scoring/add`, - fetchScore: `${CONSTANTS.SCORING_SERVICE_API_BASE}/action/scoring/fetch`, - fetchTemplate: (templateId: string) => `${CONSTANTS.SCORING_SERVICE_API_BASE}/action/scoring/getTemplate/${templateId}`, + calculateScoreEndPoint: `${CONSTANTS.KONG_API_BASE}/action/scoring/add`, + fetchScore: `${CONSTANTS.KONG_API_BASE}/action/scoring/fetch`, + fetchTemplate: (templateId: string) => `${CONSTANTS.KONG_API_BASE}/action/scoring/getTemplate/${templateId}`, } export const scoringApi = Router() @@ -30,8 +31,11 @@ scoringApi.post('/calculate', async (req, res) => { { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, org: orgValue, rootOrg: rootOrgValue, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, } ) @@ -60,8 +64,11 @@ scoringApi.post('/fetch', async (req, res) => { { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, org: orgValue, rootOrg: rootOrgValue, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, } ) @@ -88,8 +95,11 @@ scoringApi.get('/getTemplate/:templateId', async (req, res) => { const response = await axios.get(API_END_POINTS.fetchTemplate(templateId), { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, org: orgValue, rootOrg: rootOrgValue, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, }) res.status(response.status).send(response.data) From cd4f5fce1c63588619824948bc5a5a61b8c636ee Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Wed, 14 Jul 2021 15:15:26 +0530 Subject: [PATCH 017/856] Added api version on scoring service --- src/protectedApi_v8/scoring.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/protectedApi_v8/scoring.ts b/src/protectedApi_v8/scoring.ts index 5cf6270f..f7827730 100644 --- a/src/protectedApi_v8/scoring.ts +++ b/src/protectedApi_v8/scoring.ts @@ -7,9 +7,9 @@ import { ERROR } from '../utils/message' import { extractUserToken } from '../utils/requestExtract' const API_END_POINTS = { - calculateScoreEndPoint: `${CONSTANTS.KONG_API_BASE}/action/scoring/add`, - fetchScore: `${CONSTANTS.KONG_API_BASE}/action/scoring/fetch`, - fetchTemplate: (templateId: string) => `${CONSTANTS.KONG_API_BASE}/action/scoring/getTemplate/${templateId}`, + calculateScoreEndPoint: `${CONSTANTS.KONG_API_BASE}/scoring/v1/add`, + fetchScore: `${CONSTANTS.KONG_API_BASE}/scoring/v1/fetch`, + fetchTemplate: (templateId: string) => `${CONSTANTS.KONG_API_BASE}/scoring/v1/getTemplate/${templateId}`, } export const scoringApi = Router() From 02e7a157bd13747cf3f5ccec6870fda486c1b695 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Thu, 15 Jul 2021 12:47:01 +0530 Subject: [PATCH 018/856] api manager changes --- src/protectedApi_v8/connections.ts | 42 +++++++++++++++++++---- src/protectedApi_v8/network.ts | 30 +++++++++++++--- src/protectedApi_v8/user/auto-complete.ts | 10 ++++-- 3 files changed, 69 insertions(+), 13 deletions(-) diff --git a/src/protectedApi_v8/connections.ts b/src/protectedApi_v8/connections.ts index 4b2981b4..257c07fa 100644 --- a/src/protectedApi_v8/connections.ts +++ b/src/protectedApi_v8/connections.ts @@ -5,18 +5,19 @@ import { CONSTANTS } from '../utils/env' import { logError, logInfo } from '../utils/logger' import { ERROR } from '../utils/message' import { extractUserIdFromRequest } from '../utils/requestExtract' +import { extractUserToken } from '../utils/requestExtract' const unknown = 'Connections Apis:- Failed due to unknown reason' const apiEndpoints = { detail: `${CONSTANTS.USER_PROFILE_API_BASE}/user/multi-fetch/wid`, - getConnectionEstablishedData: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/connections/profile/fetch/established`, - getConnectionRequestsData: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/connections/profile/fetch/requested`, - getConnectionRequestsReceivedData: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/connections/profile/fetch/requests/received`, - getConnectionSuggestsData: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/connections/profile/find/suggests`, + getConnectionEstablishedData: `${CONSTANTS.KONG_API_BASE}/connections/profile/fetch/established`, + getConnectionRequestsData: `${CONSTANTS.KONG_API_BASE}/connections/profile/fetch/requested`, + getConnectionRequestsReceivedData: `${CONSTANTS.KONG_API_BASE}/connections/profile/fetch/requests/received`, + getConnectionSuggestsData: `${CONSTANTS.KONG_API_BASE}/connections/profile/find/suggests`, getUserRegistryById: (userId: string) => `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/search/profile?userId=${userId}`, - postConnectionAddData: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/connections/add`, - postConnectionRecommendationData: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/connections/profile/find/recommended`, - postConnectionUpdateData: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/connections/update`, + postConnectionAddData: `${CONSTANTS.KONG_API_BASE}/connections/add`, + postConnectionRecommendationData: `${CONSTANTS.KONG_API_BASE}/connections/profile/find/recommended`, + postConnectionUpdateData: `${CONSTANTS.KONG_API_BASE}/connections/update`, } export const connectionsApi = Router() @@ -37,8 +38,11 @@ connectionsApi.get('/connections/requested', async (req, res) => { const response = await axios.get(apiEndpoints.getConnectionRequestsData, { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, rootOrg, userId, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, }) res.send((response.data)) @@ -69,8 +73,11 @@ connectionsApi.get('/connections/requests/received', async (req, res) => { const response = await axios.get(apiEndpoints.getConnectionRequestsReceivedData, { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, rootOrg, userId, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, }) res.send((response.data)) @@ -101,8 +108,11 @@ connectionsApi.get('/connections/established', async (req, res) => { const response = await axios.get(apiEndpoints.getConnectionEstablishedData, { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, rootOrg, userId, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, }) res.send((response.data)) @@ -133,8 +143,11 @@ connectionsApi.get('/connections/established/:id', async (req, res) => { const response = await axios.get(apiEndpoints.getConnectionEstablishedData, { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, rootOrg, userId, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, }) res.send((response.data)) @@ -165,8 +178,11 @@ connectionsApi.get('/connections/suggests', async (req, res) => { const response = await axios.get(apiEndpoints.getConnectionSuggestsData, { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, rootOrg, userId, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, }) res.send((response.data)) @@ -216,7 +232,10 @@ connectionsApi.post('/add/connection', async (req, res) => { { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, } ) @@ -270,7 +289,10 @@ connectionsApi.post('/update/connection', async (req, res) => { { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, } ) @@ -307,8 +329,11 @@ connectionsApi.post('/connections/recommended', async (req, res) => { { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, rootOrg, userId, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, } ) @@ -371,8 +396,11 @@ connectionsApi.post('/connections/recommended/userDepartment', async (req, res) { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, rootOrg, userId, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, } ) diff --git a/src/protectedApi_v8/network.ts b/src/protectedApi_v8/network.ts index ec4b906b..3aad2304 100644 --- a/src/protectedApi_v8/network.ts +++ b/src/protectedApi_v8/network.ts @@ -5,14 +5,15 @@ import { CONSTANTS } from '../utils/env' import { logError, logInfo } from '../utils/logger' import { ERROR } from '../utils/message' import { extractUserIdFromRequest } from '../utils/requestExtract' +import { extractUserToken } from '../utils/requestExtract' const unknown = 'Network Apis:- Failed due to unknown reason' const apiEndpoints = { detail: `${CONSTANTS.USER_PROFILE_API_BASE}/user/multi-fetch/wid`, - getConnectionEstablishedData: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/connections/profile/fetch/established`, - getConnectionRequestsData: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/connections/profile/fetch/requested`, - getConnectionRequestsReceivedData: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/connections/profile/fetch/requests/received`, - getConnectionSuggestsData: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/connections/profile/find/suggests`, + getConnectionEstablishedData: `${CONSTANTS.KONG_API_BASE}/connections/profile/fetch/established`, + getConnectionRequestsData: `${CONSTANTS.KONG_API_BASE}/connections/profile/fetch/requested`, + getConnectionRequestsReceivedData: `${CONSTANTS.KONG_API_BASE}/connections/profile/fetch/requests/received`, + getConnectionSuggestsData: `${CONSTANTS.KONG_API_BASE}/connections/profile/find/suggests`, postConnectionAddData: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/connections/add`, postConnectionRecommendationData: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/connections/profile/find/recommended`, postConnectionUpdateData: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/connections/update`, @@ -37,8 +38,11 @@ networkConnectionApi.get('/connections/requested', async (req, res) => { const response = await axios.get(apiEndpoints.getConnectionRequestsData, { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, rootOrg, userId, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, }) res.send((response.data)) @@ -69,8 +73,11 @@ networkConnectionApi.get('/connections/requests/received', async (req, res) => { const response = await axios.get(apiEndpoints.getConnectionRequestsReceivedData, { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, rootOrg, userId, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, }) res.send((response.data)) @@ -101,8 +108,11 @@ networkConnectionApi.get('/connections/established', async (req, res) => { const response = await axios.get(apiEndpoints.getConnectionEstablishedData, { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, rootOrg, userId, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, }) res.send((response.data)) @@ -133,8 +143,11 @@ networkConnectionApi.get('/connections/established/:id', async (req, res) => { const response = await axios.get(apiEndpoints.getConnectionEstablishedData, { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, rootOrg, userId, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, }) res.send((response.data)) @@ -165,8 +178,11 @@ networkConnectionApi.get('/connections/suggests', async (req, res) => { const response = await axios.get(apiEndpoints.getConnectionSuggestsData, { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, rootOrg, userId, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, }) res.send((response.data)) @@ -206,7 +222,10 @@ networkConnectionApi.post('/add/connection', async (req, res) => { { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, } ) @@ -248,7 +267,10 @@ networkConnectionApi.post('/update/connection', async (req, res) => { { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, } ) diff --git a/src/protectedApi_v8/user/auto-complete.ts b/src/protectedApi_v8/user/auto-complete.ts index 5f786a6e..41b79152 100644 --- a/src/protectedApi_v8/user/auto-complete.ts +++ b/src/protectedApi_v8/user/auto-complete.ts @@ -3,10 +3,11 @@ import { Router } from 'express' import { axiosRequestConfig } from '../../configs/request.config' import { CONSTANTS } from '../../utils/env' import { ERROR } from '../../utils/message' +import { extractUserToken } from '../../utils/requestExtract' const API_END_POINTS = { users: (queryParams: string) => - `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/autocomplete?${queryParams}`, + `${CONSTANTS.KONG_API_BASE}/v1/user/autocomplete?${queryParams}`, usersByDepartment: (rootOrg: string, searchItem: string) => `${CONSTANTS.USER_PROFILE_API_BASE}/user/autocomplete/${rootOrg}/department/${searchItem}`, } @@ -47,7 +48,12 @@ autocompleteApi.get('/:query', async (req, res) => { const url = API_END_POINTS.users('searchString=' + queryParams) const response = await axios({ ...axiosRequestConfig, - headers: { rootOrg }, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), + }, method: 'GET', url, }) From 2e976c337e21aba704164e427a83e655b8df7dea Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Thu, 15 Jul 2021 17:11:30 +0530 Subject: [PATCH 019/856] Work flow handler kong api changes --- src/protectedApi_v8/workflow-handler.ts | 44 ++++++++++++++++++++----- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/src/protectedApi_v8/workflow-handler.ts b/src/protectedApi_v8/workflow-handler.ts index 18d2d583..57772c78 100644 --- a/src/protectedApi_v8/workflow-handler.ts +++ b/src/protectedApi_v8/workflow-handler.ts @@ -5,20 +5,21 @@ import { axiosRequestConfig } from '../configs/request.config' import { CONSTANTS } from '../utils/env' import { logError } from '../utils/logger' import { ERROR } from '../utils/message' +import { extractUserToken } from '../utils/requestExtract' const API_END_POINTS = { - applicationTransition: `${CONSTANTS.WORKFLOW_HANDLER_SERVICE_API_BASE}/v1/workflow/transition`, - applicationsSearch: `${CONSTANTS.WORKFLOW_HANDLER_SERVICE_API_BASE}/v1/workflow/applications/search`, + applicationTransition: `${CONSTANTS.KONG_API_BASE}/workflow/transition`, + applicationsSearch: `${CONSTANTS.KONG_API_BASE}/workflow/applications/search`, historyBasedOnApplicationId: (applicationId: string) => `${CONSTANTS.WORKFLOW_HANDLER_SERVICE_API_BASE}/v1/workflow/${applicationId}/history`, historyBasedOnWfId: (workflowId: string, applicationId: string) => `${CONSTANTS.WORKFLOW_HANDLER_SERVICE_API_BASE}/v1/workflow/${workflowId}/${applicationId}/history`, nextActionSearch: (serviceName: string, state: string) => - `${CONSTANTS.WORKFLOW_HANDLER_SERVICE_API_BASE}/v1/workflow/nextAction/${serviceName}/${state}`, - userProfileUpdate: `${CONSTANTS.WORKFLOW_HANDLER_SERVICE_API_BASE}/v1/workflow/updateUserProfileWF`, - userWfFieldsSearch: `${CONSTANTS.WORKFLOW_HANDLER_SERVICE_API_BASE}/v1/workflow/getUserWFApplicationFields`, - userWfSearch: `${CONSTANTS.WORKFLOW_HANDLER_SERVICE_API_BASE}/v1/workflow/getUserWF`, - workflowProcess: (wfId: string) => `${CONSTANTS.WORKFLOW_HANDLER_SERVICE_API_BASE}/v1/workflow/workflowProcess/${wfId}`, + `${CONSTANTS.KONG_API_BASE}/workflow/nextAction/${serviceName}/${state}`, + userProfileUpdate: `${CONSTANTS.KONG_API_BASE}/workflow/updateUserProfileWF`, + userWfFieldsSearch: `${CONSTANTS.KONG_API_BASE}/workflow/getUserWFApplicationFields`, + userWfSearch: `${CONSTANTS.KONG_API_BASE}/workflow/getUserWF`, + workflowProcess: (wfId: string) => `${CONSTANTS.KONG_API_BASE}/workflow/workflowProcess/${wfId}`, } export const workflowHandlerApi = Router() @@ -39,8 +40,11 @@ workflowHandlerApi.post('/transition', async (req, res) => { { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, org: orgValue, rootOrg: rootOrgValue, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, } ) @@ -69,9 +73,11 @@ workflowHandlerApi.post('/applicationsSearch', async (req, res) => { { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, org: orgValue, rootOrg: rootOrgValue, - + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, } ) @@ -95,9 +101,11 @@ workflowHandlerApi.get('/nextActionSearch/:serviceName/:state', async (req, res) const response = await axios.get(API_END_POINTS.nextActionSearch(serviceName, state), { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, org: orgValue, rootOrg: rootOrgValue, - + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, }) res.status(response.status).send(response.data) @@ -120,8 +128,11 @@ workflowHandlerApi.get('/historyByApplicationIdAndWfId/:applicationId/:wfId', as const response = await axios.get(API_END_POINTS.historyBasedOnWfId(wfId, applicationId), { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, org: orgValue, rootOrg: rootOrgValue, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, }) res.status(response.status).send(response.data) @@ -142,7 +153,10 @@ workflowHandlerApi.get('/workflowProcess/:wfId', async (req, res) => { const response = await axios.get(API_END_POINTS.workflowProcess(wfId), { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, rootOrg: rootOrgValue, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, }) res.status(response.status).send(response.data) @@ -164,8 +178,11 @@ workflowHandlerApi.get('/historyByApplicationId/:applicationId', async (req, res const response = await axios.get(API_END_POINTS.historyBasedOnApplicationId(applicationId), { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, org: orgValue, rootOrg: rootOrgValue, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, }) res.status(response.status).send(response.data) @@ -193,8 +210,11 @@ workflowHandlerApi.post('/updateUserProfileWf', async (req, res) => { { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, org: orgValue, rootOrg: rootOrgValue, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, } ) @@ -224,9 +244,12 @@ workflowHandlerApi.post('/userWfSearch', async (req, res) => { { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, org: orgValue, rootOrg: rootOrgValue, wid, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, } ) @@ -256,9 +279,12 @@ workflowHandlerApi.post('/userWFApplicationFieldsSearch', async (req, res) => { { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, org: orgValue, rootOrg: rootOrgValue, wid, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, } ) From b7e3c5ac877e3f00f0eae24e602fe999b16cd6d7 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Fri, 16 Jul 2021 14:17:32 +0530 Subject: [PATCH 020/856] Added Proxy API for org management --- src/proxies_v8/proxies_v8.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 060fd761..19088a80 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -209,6 +209,10 @@ proxiesV8.use('/notification/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) +proxiesV8.use('/org/*', + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + // proxiesV8.use('/api/framework/*', // // tslint:disable-next-line: max-line-length // proxyCreatorQML(express.Router(), `${CONSTANTS.KONG_API_BASE}`, '/api/') From da211a71c42081458af1d1affe08926d1cd753b0 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Tue, 20 Jul 2021 16:02:42 +0530 Subject: [PATCH 021/856] Changes for cors --- src/server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server.ts b/src/server.ts index e876008c..cc431056 100644 --- a/src/server.ts +++ b/src/server.ts @@ -39,6 +39,7 @@ export class Server { protected app = express() private keycloak?: CustomKeycloak private constructor() { + this.app.use(cors({origin: 'http://local.igot-dev.in:3000', credentials: true})) const sessionConfig = getSessionConfig() this.app.use(expressSession(sessionConfig)) this.app.all('*', apiWhiteListLogger()) @@ -78,7 +79,6 @@ export class Server { this.app.use(express.json({ limit: '50mb' })) this.app.use(fileUpload()) // this.app.use(cors()) - this.app.use(cors({origin: 'http://local.igot-dev.in:3000', credentials: true})) this.app.use('/healthcheck', healthcheck({ healthy() { return { everything: 'is ok' } From 73eeed05d63b4b1967867adc3fd33e447a19f94c Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Tue, 20 Jul 2021 18:49:20 +0530 Subject: [PATCH 022/856] sb-cb-ext kong changes --- src/protectedApi_v8/catalog.ts | 8 +- src/protectedApi_v8/cohorts.ts | 40 +++++----- src/protectedApi_v8/user/mandatoryContent.ts | 5 +- src/protectedApi_v8/workallocation.ts | 79 ++++++++++++++------ 4 files changed, 83 insertions(+), 49 deletions(-) diff --git a/src/protectedApi_v8/catalog.ts b/src/protectedApi_v8/catalog.ts index ef6c4303..ed97ba22 100644 --- a/src/protectedApi_v8/catalog.ts +++ b/src/protectedApi_v8/catalog.ts @@ -7,22 +7,22 @@ import { IFilterUnitContent } from '../models/catalog.model' import { getFilters, getFilterUnitByType } from '../service/catalog' import { logError } from '../utils/logger' import { ERROR } from '../utils/message' -import { extractAuthorizationFromRequest, extractUserIdFromRequest } from '../utils/requestExtract' +import {extractUserIdFromRequest, extractUserToken} from '../utils/requestExtract' export const catalogApi = Router() const API_END_POINTS = { - getCatalogEndPoint: `${CONSTANTS.SB_EXT_API_BASE_2}/v1/catalog/`, + getCatalogEndPoint: `${CONSTANTS.KONG_API_BASE}/v1/catalog/`, } const failedToProcess = 'Failed to process the request. ' catalogApi.get('/', async (req, res) => { try { - const xAuth = extractAuthorizationFromRequest(req).split(' ') const response = await axios.get(API_END_POINTS.getCatalogEndPoint, { ...axiosRequestConfig, headers: { - xAuthUser: xAuth[1], + Authorization: CONSTANTS.SB_API_KEY, + 'x-authenticated-user-token': extractUserToken(req), }, }) res.status(response.status).send(response.data) diff --git a/src/protectedApi_v8/cohorts.ts b/src/protectedApi_v8/cohorts.ts index d58c9650..4f84acbd 100644 --- a/src/protectedApi_v8/cohorts.ts +++ b/src/protectedApi_v8/cohorts.ts @@ -4,12 +4,12 @@ import { axiosRequestConfig } from '../configs/request.config' import { CONSTANTS } from '../utils/env' import { logError } from '../utils/logger' import { ERROR } from '../utils/message' -import { extractUserIdFromRequest } from '../utils/requestExtract' +import { extractAuthorizationFromRequest, extractUserIdFromRequest, extractUserToken } from '../utils/requestExtract' const API_END_POINTS = { addTemplate: `https://igot-dev.in/api/course/batch/cert/v1/template/add`, - autoenrollment: (userId: string, courseId: string) => `${CONSTANTS.COHORTS_API_BASE}/v1/autoenrollment/${userId}/${courseId}`, - cohorts: `${CONSTANTS.COHORTS_API_BASE}/v2/resources`, + autoenrollment: `${CONSTANTS.KONG_API_BASE}/v1/autoenrollment`, + cohorts: `${CONSTANTS.KONG_API_BASE}/v2/resources`, downloadCert: (certId: string) => `https://igot-dev.in/api/certreg/v2/certs/download/${certId}`, groupCohorts: (groupId: number) => `${CONSTANTS.USER_PROFILE_API_BASE}/groups/${groupId}/users `, @@ -41,24 +41,25 @@ cohortsApi.get('/:cohortType/:contentId', async (req, res) => { } const org = req.header('org') const rootOrgValue = req.header('rootOrg') - const auth = req.header('Authorization') as string if (!org || !rootOrgValue) { res.status(400).send(ERROR.ERROR_NO_ORG_DATA) return } if (cohortType === 'authors') { const host = req.protocol + '://' + req.get('host') - const userList = await getAuthorsDetails(host, auth, contentId) + const userList = await getAuthorsDetails(host, extractAuthorizationFromRequest(req), contentId) res.status(200).send(userList) } else { - const url = `${API_END_POINTS.cohorts}/${contentId}/user/${extractUserIdFromRequest( - req - )}/cohorts/${cohortType}` + const url = `${API_END_POINTS.cohorts}/user/cohorts/${cohortType}` const response = await axios({ ...axiosRequestConfig, headers: { - Authorization: auth, + Authorization: CONSTANTS.SB_API_KEY, + resourceId: contentId, rootOrg: rootOrgValue, + userUUID: extractUserIdFromRequest(req), + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), }, method: 'GET', url, @@ -144,12 +145,15 @@ cohortsApi.get('/user/autoenrollment/:courseId', async (req, res) => { const courseId = req.params.courseId const wid = req.headers.wid as string const rootOrgValue = req.headers.rootorg - const auth = req.header('Authorization') as string - const response = await axios.get(API_END_POINTS.autoenrollment(wid, courseId), { + const response = await axios.get(API_END_POINTS.autoenrollment, { ...axiosRequestConfig, headers: { - Authorization: auth, + Authorization: CONSTANTS.SB_API_KEY, + courseId, rootOrg: rootOrgValue, + userUUID: wid, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), }, }) res.status(response.status).send(response.data) @@ -166,14 +170,12 @@ cohortsApi.get('/user/autoenrollment/:courseId', async (req, res) => { cohortsApi.patch('/course/batch/cert/template/add', async (req, res) => { try { const template = req.body - const auth = req.header('Authorization') as string - const token = auth.split(' ')[1] const response = await axios.patch(API_END_POINTS.addTemplate, template, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.CERT_AUTH_TOKEN, /* tslint:disable-next-line */ - 'x-authenticated-user-token': token, + 'x-authenticated-user-token': extractUserToken(req), }, }) @@ -192,14 +194,12 @@ cohortsApi.patch('/course/batch/cert/template/add', async (req, res) => { cohortsApi.post('/course/batch/cert/issue', async (req, res) => { try { const template = req.body - const auth = req.header('Authorization') as string - const token = auth.split(' ')[1] const response = await axios.post(API_END_POINTS.issueCert, template, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.CERT_AUTH_TOKEN, /* tslint:disable-next-line */ - 'x-authenticated-user-token': token, + 'x-authenticated-user-token' : extractUserToken(req), }, }) @@ -218,14 +218,12 @@ cohortsApi.post('/course/batch/cert/issue', async (req, res) => { cohortsApi.get('/course/batch/cert/download/:certId', async (req, res) => { try { const certId = req.params.certId - const auth = req.header('Authorization') as string - const token = auth.split(' ')[1] const response = await axios.get(API_END_POINTS.downloadCert(certId), { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.CERT_AUTH_TOKEN, /* tslint:disable-next-line */ - 'x-authenticated-user-token': token, + 'x-authenticated-user-token' : extractUserToken(req), }, }) diff --git a/src/protectedApi_v8/user/mandatoryContent.ts b/src/protectedApi_v8/user/mandatoryContent.ts index 3c543ba3..d70eeb5f 100644 --- a/src/protectedApi_v8/user/mandatoryContent.ts +++ b/src/protectedApi_v8/user/mandatoryContent.ts @@ -4,13 +4,14 @@ import { axiosRequestConfig } from '../../configs/request.config' import { CONSTANTS } from '../../utils/env' import { logError } from '../../utils/logger' import { ERROR } from '../../utils/message' +import { extractUserToken } from '../../utils/requestExtract' import { extractAuthorizationFromRequest } from '../../utils/requestExtract' const API_END_POINTS = { - mandatoryContentStatus: `${CONSTANTS.SB_EXT_API_BASE_2}/v1/check/mandatoryContentStatus`, + mandatoryContentStatus: `${CONSTANTS.KONG_API_BASE}/v1/check/mandatoryContentStatus`, } export const mandatoryContent = Router() @@ -30,9 +31,11 @@ mandatoryContent.get('/checkStatus', async (req, res) => { const response = await axios.get(API_END_POINTS.mandatoryContentStatus, { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, org: orgValue, rootOrg: rootOrgValue, wid: widValue, + 'x-authenticated-user-token': extractUserToken(req), xAuthUser: xAuth[1], }, }) diff --git a/src/protectedApi_v8/workallocation.ts b/src/protectedApi_v8/workallocation.ts index 818afb79..63f89be2 100644 --- a/src/protectedApi_v8/workallocation.ts +++ b/src/protectedApi_v8/workallocation.ts @@ -5,25 +5,25 @@ import { axiosRequestConfig } from '../configs/request.config' import { CONSTANTS } from '../utils/env' import { logError } from '../utils/logger' import { ERROR } from '../utils/message' -import { extractUserIdFromRequest } from '../utils/requestExtract' +import { extractAuthorizationFromRequest, extractUserIdFromRequest, extractUserToken } from '../utils/requestExtract' const workallocationV1Path = 'v1/workallocation' const workallocationV2Path = 'v2/workallocation' const API_END_POINTS = { - addAllocationEndPoint: (path: string) => `${CONSTANTS.SB_EXT_API_BASE_2}/${path}/add`, - addWorkOrderEndPoint: (path: string) => `${CONSTANTS.SB_EXT_API_BASE_2}/${path}/add/workorder`, - copyWorkOrderEndPoint: (path: string) => `${CONSTANTS.SB_EXT_API_BASE_2}/${path}/copy/workOrder`, - getPdf: (id: string) => `${CONSTANTS.SB_EXT_API_BASE_2}/getWOPdf/${id}`, - getUserBasicDetails: (userId: string) => `${CONSTANTS.SB_EXT_API_BASE_2}/${workallocationV2Path}/user/basicInfo/${userId}`, + addAllocationEndPoint: (path: string) => `${CONSTANTS.KONG_API_BASE}/${path}/add`, + addWorkOrderEndPoint: (path: string) => `${CONSTANTS.KONG_API_BASE}/${path}/add/workorder`, + copyWorkOrderEndPoint: (path: string) => `${CONSTANTS.KONG_API_BASE}/${path}/copy/workOrder`, + getPdf: (id: string) => `${CONSTANTS.KONG_API_BASE}/getWOPdf/${id}`, + getUserBasicDetails: (userId: string) => `${CONSTANTS.KONG_API_BASE}/${workallocationV2Path}/user/basicInfo/${userId}`, getUsersEndPoint: `${CONSTANTS.SB_EXT_API_BASE_2}/v1/workallocation/getUsers`, - getWorkAllocationById: (path: string, id: string) => `${CONSTANTS.SB_EXT_API_BASE_2}/${path}/getWorkAllocationById/${id}`, - getWorkOrderById: (path: string, id: string) => `${CONSTANTS.SB_EXT_API_BASE_2}/${path}/getWorkOrderById/${id}`, - getWorkOrders: (path: string) => `${CONSTANTS.SB_EXT_API_BASE_2}/${path}/getWorkOrders`, + getWorkAllocationById: (path: string, id: string) => `${CONSTANTS.KONG_API_BASE}/${path}/getWorkAllocationById/${id}`, + getWorkOrderById: (path: string, id: string) => `${CONSTANTS.KONG_API_BASE}/${path}/getWorkOrderById/${id}`, + getWorkOrders: (path: string) => `${CONSTANTS.KONG_API_BASE}/${path}/getWorkOrders`, updateAllocationEndPoint: `${CONSTANTS.SB_EXT_API_BASE_2}/v1/workallocation/update`, - updateWorkAllocationEndPoint: (path: string) => `${CONSTANTS.SB_EXT_API_BASE_2}/${path}/update`, - updateWorkOrder: (path: string) => `${CONSTANTS.SB_EXT_API_BASE_2}/${path}/update/workorder`, + updateWorkAllocationEndPoint: (path: string) => `${CONSTANTS.KONG_API_BASE}/${path}/update`, + updateWorkOrder: (path: string) => `${CONSTANTS.KONG_API_BASE}/${path}/update/workorder`, userAutoCompleteEndPoint: (searchTerm: string) => - `${CONSTANTS.SB_EXT_API_BASE_2}/v1/workallocation/users/autocomplete?searchTerm=${searchTerm}`, + `${CONSTANTS.KONG_API_BASE}/v1/workallocation/users/autocomplete?searchTerm=${searchTerm}`, } export const workAllocationApi = Router() @@ -46,7 +46,7 @@ workAllocationApi.post('/add', async (req, res) => { { ...axiosRequestConfig, headers: { - Authorization: req.header('Authorization'), + Authorization: extractAuthorizationFromRequest(req), userId, }, } @@ -75,7 +75,7 @@ workAllocationApi.post('/update', async (req, res) => { { ...axiosRequestConfig, headers: { - Authorization: req.header('Authorization'), + Authorization: extractAuthorizationFromRequest(req), userId, }, } @@ -119,7 +119,9 @@ workAllocationApi.get('/user/autocomplete/:searchTerm', async (req, res) => { const response = await axios.get(API_END_POINTS.userAutoCompleteEndPoint(searchTerm), { ...axiosRequestConfig, headers: { - + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), }, }) res.status(response.status).send(response.data) @@ -148,8 +150,11 @@ workAllocationApi.post('/v2/add', async (req, res) => { { ...axiosRequestConfig, headers: { - Authorization: req.header('Authorization'), + Authorization: CONSTANTS.SB_API_KEY, userId, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), + }, } ) @@ -177,8 +182,10 @@ workAllocationApi.post('/v2/update', async (req, res) => { { ...axiosRequestConfig, headers: { - Authorization: req.header('Authorization'), + Authorization: CONSTANTS.SB_API_KEY, userId, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), }, } ) @@ -205,7 +212,10 @@ workAllocationApi.post('/add/workorder', async (req, res) => { { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, userId, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), }, } ) @@ -226,15 +236,16 @@ workAllocationApi.post('/update/workorder', async (req, res) => { res.status(400).send(userIdFailedMessage) return } - const auth = req.header('Authorization') as string const response = await axios.post( API_END_POINTS.updateWorkOrder(workallocationV2Path), req.body, { ...axiosRequestConfig, headers: { - Authorization: auth, + Authorization: CONSTANTS.SB_API_KEY, userId, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), }, } ) @@ -256,7 +267,11 @@ workAllocationApi.post('/getWorkOrders', async (req, res) => { req.body, { ...axiosRequestConfig, - headers: req.headers, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), + }, } ) res.status(response.status).send(response.data) @@ -281,7 +296,11 @@ workAllocationApi.get('/getWorkOrderById/:workOrderId', async (req, res) => { API_END_POINTS.getWorkOrderById(workallocationV2Path, workOrderId), { ...axiosRequestConfig, - headers: req.headers, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), + }, } ) res.status(response.status).send(response.data) @@ -306,7 +325,11 @@ workAllocationApi.get('/getWorkAllocationById/:workAllocationId', async (req, re API_END_POINTS.getWorkAllocationById(workallocationV2Path, workAllocationId), { ...axiosRequestConfig, - headers: req.headers, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), + }, } ) res.status(response.status).send(response.data) @@ -333,7 +356,10 @@ workAllocationApi.post('/copy/workOrder', async (req, res) => { { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, userId, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), }, } ) @@ -359,7 +385,11 @@ workAllocationApi.get('/getUserBasicInfo/:userId', async (req, res) => { API_END_POINTS.getUserBasicDetails(userId), { ...axiosRequestConfig, - headers: req.headers, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), + }, } ) res.status(response.status).send(response.data) @@ -386,6 +416,9 @@ workAllocationApi.get('/getWOPdf/:workOrderId', async (req, res) => { ...axiosRequestConfig, headers: { Accept: 'application/pdf', + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), }, responseType: 'arraybuffer', From 8f46f4d399f0957d5fe11168e501d03438bf7843 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Wed, 21 Jul 2021 11:44:57 +0530 Subject: [PATCH 023/856] frac api's authroization issue --- src/protectedApi_v8/competency.ts | 8 +++++--- src/protectedApi_v8/frac.ts | 15 ++++++++------- src/protectedApi_v8/roleActivity.ts | 6 ++++-- src/protectedApi_v8/user/evaluate.ts | 9 ++++++--- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/protectedApi_v8/competency.ts b/src/protectedApi_v8/competency.ts index 56fd4ba4..63288719 100644 --- a/src/protectedApi_v8/competency.ts +++ b/src/protectedApi_v8/competency.ts @@ -4,6 +4,8 @@ import { Router } from 'express' import { axiosRequestConfig } from '../configs/request.config' import { CONSTANTS } from '../utils/env' import { ERROR } from '../utils/message' +import { extractAuthorizationFromRequest } from '../utils/requestExtract' + const API_END_POINTS = { addCompetency: `${CONSTANTS.FRAC_API_BASE}/api/frac/addDataNode`, @@ -17,7 +19,7 @@ const unknownError = 'Failed due to unknown reason' competencyApi.get('/getCompetency', async (req, res) => { try { const rootOrg = req.header('rootOrg') - const authToken = req.header('Authorization') + const authToken = extractAuthorizationFromRequest(req) if (!rootOrg) { res.status(400).send(ERROR.ERROR_NO_ORG_DATA) return @@ -40,7 +42,7 @@ competencyApi.get('/getCompetency', async (req, res) => { competencyApi.post('/addCompetency', async (req, res) => { try { - const authToken = req.header('Authorization') + const authToken = extractAuthorizationFromRequest(req) const response = await axios.post(API_END_POINTS.addCompetency, req.body, { ...axiosRequestConfig, headers: { @@ -59,7 +61,7 @@ competencyApi.post('/addCompetency', async (req, res) => { competencyApi.post('/searchCompetency', async (req, res) => { try { - const authToken = req.header('Authorization') + const authToken = extractAuthorizationFromRequest(req) const response = await axios.post(API_END_POINTS.searchCompetency, req.body, { ...axiosRequestConfig, headers: { diff --git a/src/protectedApi_v8/frac.ts b/src/protectedApi_v8/frac.ts index 710d614a..ccdb1826 100644 --- a/src/protectedApi_v8/frac.ts +++ b/src/protectedApi_v8/frac.ts @@ -5,6 +5,7 @@ import { axiosRequestConfig } from '../configs/request.config' import { CONSTANTS } from '../utils/env' import { logError, logInfo } from '../utils/logger' import { ERROR } from '../utils/message' +import { extractAuthorizationFromRequest } from '../utils/requestExtract' const API_END_POINTS = { addDataNode: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/addDataNode`, @@ -45,7 +46,7 @@ fracApi.get('/getAllNodes/:type', async (req, res) => { const response = await axios.get(apiEndPoint, { ...axiosRequestConfig, headers: { - Authorization: req.header('Authorization'), + Authorization: extractAuthorizationFromRequest(req), }, }) res.status(response.status).send(response.data) @@ -63,7 +64,7 @@ fracApi.post('/addDataNode', async (req, res) => { const response = await axios.post(API_END_POINTS.addDataNode, req.body, { ...axiosRequestConfig, headers: { - Authorization: req.header('Authorization'), + Authorization: extractAuthorizationFromRequest(req), }, }) res.status(response.status).send(response.data) @@ -81,7 +82,7 @@ fracApi.post('/addDataNodeBulk', async (req, res) => { const response = await axios.post(API_END_POINTS.addDataNodeBulk, req.body, { ...axiosRequestConfig, headers: { - Authorization: req.header('Authorization'), + Authorization: extractAuthorizationFromRequest(req), }, }) res.status(response.status).send(response.data) @@ -99,7 +100,7 @@ fracApi.post('/searchNodes', async (req, res) => { const response = await axios.post(API_END_POINTS.searchNodes, req.body, { ...axiosRequestConfig, headers: { - Authorization: req.header('Authorization'), + Authorization: extractAuthorizationFromRequest(req), }, }) res.status(response.status).send(response.data) @@ -119,7 +120,7 @@ fracApi.get('/getNodeById/:id/:type', async (req, res) => { const response = await axios.get(API_END_POINTS.getNodeById(id, type), { ...axiosRequestConfig, headers: { - Authorization: req.header('Authorization'), + Authorization: extractAuthorizationFromRequest(req), }, }) res.status(response.status).send(response.data) @@ -135,7 +136,7 @@ fracApi.get('/getNodeById/:id/:type', async (req, res) => { fracApi.get('/:type/:key', async (req, res) => { try { const rootOrg = req.header('rootOrg') - const authToken = req.header('Authorization') + const authToken = extractAuthorizationFromRequest(req) if (!rootOrg || !authToken) { res.status(400).send(ERROR.ERROR_NO_ORG_DATA) return @@ -164,7 +165,7 @@ fracApi.get('/:type/:key', async (req, res) => { const response = await axios.post(API_END_POINTS.searchNodes, searchBody, { ...axiosRequestConfig, headers: { - Authorization: req.header('Authorization'), + Authorization: extractAuthorizationFromRequest(req), }, }) res.status(200).send(response.data) diff --git a/src/protectedApi_v8/roleActivity.ts b/src/protectedApi_v8/roleActivity.ts index 4dacb455..427abbf5 100644 --- a/src/protectedApi_v8/roleActivity.ts +++ b/src/protectedApi_v8/roleActivity.ts @@ -6,6 +6,8 @@ import { ERROR } from '../utils/message' import axios from 'axios' import { axiosRequestConfig } from '../configs/request.config' import { CONSTANTS } from '../utils/env' +import { extractAuthorizationFromRequest } from '../utils/requestExtract' + export const roleActivityApi = Router() @@ -33,7 +35,7 @@ roleActivityApi.get('/', async (req, res) => { roleActivityApi.get('/:roleKey', async (req, res) => { try { const rootOrg = req.header('rootOrg') - const authToken = req.header('Authorization') + const authToken = extractAuthorizationFromRequest(req) if (!rootOrg || !authToken) { res.status(400).send(ERROR.ERROR_NO_ORG_DATA) return @@ -60,7 +62,7 @@ roleActivityApi.get('/:roleKey', async (req, res) => { const response = await axios.post(API_END_POINTS.searchNodes, searchBody, { ...axiosRequestConfig, headers: { - Authorization: req.header('Authorization'), + Authorization: extractAuthorizationFromRequest(req), }, }) const returnRoleList: IRole[] = [] diff --git a/src/protectedApi_v8/user/evaluate.ts b/src/protectedApi_v8/user/evaluate.ts index 88d99e20..4ff1114d 100644 --- a/src/protectedApi_v8/user/evaluate.ts +++ b/src/protectedApi_v8/user/evaluate.ts @@ -3,11 +3,11 @@ import { Router } from 'express' import { axiosRequestConfig } from '../../configs/request.config' import { CONSTANTS } from '../../utils/env' import { ERROR } from '../../utils/message' -import { extractUserIdFromRequest } from '../../utils/requestExtract' +import { extractUserIdFromRequest, extractUserToken } from '../../utils/requestExtract' const GENERAL_ERR_MSG = 'Failed due to unknown reason' const API_END_POINTS = { - assessmentSubmitV2: `${CONSTANTS.SUBMISSION_API_BASE}/v2/user`, + assessmentSubmitV2: `${CONSTANTS.KONG_API_BASE}/v2/user`, iapSubmitAssessment: `${CONSTANTS.SB_EXT_API_BASE_2}/v3/iap-assessment`, postAssessment: `${CONSTANTS.POST_ASSESSMENT_BASE}/lmsapi/v1/post_assessment`, } @@ -22,7 +22,7 @@ evaluateApi.post('/assessment/submit/v2', async (req, res) => { return } const userId = extractUserIdFromRequest(req) - const url = `${API_END_POINTS.assessmentSubmitV2}/${userId}/assessment/submit` + const url = `${API_END_POINTS.assessmentSubmitV2}/assessment/submit` const requestBody = { ...req.body, } @@ -30,7 +30,10 @@ evaluateApi.post('/assessment/submit/v2', async (req, res) => { ...axiosRequestConfig, data: requestBody, headers: { + Authorization: CONSTANTS.SB_API_KEY, rootOrg, + userId, + 'x-authenticated-user-token': extractUserToken(req), }, method: 'POST', url, From 648fb68113ece64588501149178c216d2615a266 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Wed, 21 Jul 2021 18:49:15 +0530 Subject: [PATCH 024/856] added content update proxy changes --- src/proxies_v8/proxies_v8.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 19088a80..a78c2479 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -194,7 +194,9 @@ proxiesV8.use([ ], proxyCreatorQML(express.Router(), `${CONSTANTS.KONG_API_BASE}`, '/action/') ) - +proxiesV8.use('/action/content/v3/updateReviewStatus', + proxyCreatorKnowledge(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) proxiesV8.use('/action/*', proxyCreatorKnowledge(express.Router(), `${CONSTANTS.KNOWLEDGE_MW_API_BASE}`) ) From 9be9984504da792aa41e5056d2f320a60b64f603 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Wed, 21 Jul 2021 19:04:56 +0530 Subject: [PATCH 025/856] Added SB Data APIs --- src/proxies_v8/proxies_v8.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index a78c2479..a865fdef 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -225,6 +225,10 @@ proxiesV8.use('/api/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) +proxiesV8.use('/data/*', + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + proxiesV8.use('/assets/*', // tslint:disable-next-line: max-line-length proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) From fb1b58af8e9635ed85baf89b6ed473b0d102696f Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Thu, 22 Jul 2021 10:07:55 +0530 Subject: [PATCH 026/856] Changes for cors --- src/server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server.ts b/src/server.ts index cc431056..08291078 100644 --- a/src/server.ts +++ b/src/server.ts @@ -39,7 +39,7 @@ export class Server { protected app = express() private keycloak?: CustomKeycloak private constructor() { - this.app.use(cors({origin: 'http://local.igot-dev.in:3000', credentials: true})) + this.app.use(cors({origin: 'https://local.igot-dev.in:3000', credentials: true})) const sessionConfig = getSessionConfig() this.app.use(expressSession(sessionConfig)) this.app.all('*', apiWhiteListLogger()) From 7be5fc300a8e1240946807185c3d0d0fa284860b Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Thu, 22 Jul 2021 10:10:45 +0530 Subject: [PATCH 027/856] lint fix --- src/protectedApi_v8/competency.ts | 1 - src/protectedApi_v8/roleActivity.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/protectedApi_v8/competency.ts b/src/protectedApi_v8/competency.ts index 63288719..e44d797e 100644 --- a/src/protectedApi_v8/competency.ts +++ b/src/protectedApi_v8/competency.ts @@ -6,7 +6,6 @@ import { CONSTANTS } from '../utils/env' import { ERROR } from '../utils/message' import { extractAuthorizationFromRequest } from '../utils/requestExtract' - const API_END_POINTS = { addCompetency: `${CONSTANTS.FRAC_API_BASE}/api/frac/addDataNode`, getCompetency: `${CONSTANTS.FRAC_API_BASE}/api/frac/getAllNodes?type=COMPETENCY&status=VERIFIED`, diff --git a/src/protectedApi_v8/roleActivity.ts b/src/protectedApi_v8/roleActivity.ts index 427abbf5..fb03b6e5 100644 --- a/src/protectedApi_v8/roleActivity.ts +++ b/src/protectedApi_v8/roleActivity.ts @@ -8,7 +8,6 @@ import { axiosRequestConfig } from '../configs/request.config' import { CONSTANTS } from '../utils/env' import { extractAuthorizationFromRequest } from '../utils/requestExtract' - export const roleActivityApi = Router() const API_END_POINTS = { From eec0556ab0550168ba2edd8a8cb255ac34664c3c Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Thu, 22 Jul 2021 17:10:23 +0530 Subject: [PATCH 028/856] remove header from proxycreator --- src/utils/proxyCreator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 6a492fe0..b8082882 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -31,8 +31,8 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { }) // tslint:disable-next-line: no-any -proxy.on('proxyRes', (proxyRes: any, req: any, _res: any, ) => { - +proxy.on('proxyRes', (proxyRes: any, req: any, res: any, ) => { + res.removeHeader('access-control-allow-origin') // write user session with roles if (req.originalUrl.includes('/user/v2/read')) { // tslint:disable-next-line: no-any From e049e0401da501f2a629c3da7b962f0b69c701aa Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Thu, 22 Jul 2021 17:37:45 +0530 Subject: [PATCH 029/856] Fix for proxy --- src/utils/proxyCreator.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index b8082882..d4465ab1 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -31,8 +31,9 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { }) // tslint:disable-next-line: no-any -proxy.on('proxyRes', (proxyRes: any, req: any, res: any, ) => { - res.removeHeader('access-control-allow-origin') +proxy.on('proxyRes', (proxyRes: any, req: any, _res: any, ) => { + // res.removeHeader('access-control-allow-origin') + delete proxyRes.headers['access-control-allow-origin'] // write user session with roles if (req.originalUrl.includes('/user/v2/read')) { // tslint:disable-next-line: no-any From 2f6faebff8bec63a254d22b08ee2c1424ca7705e Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Thu, 22 Jul 2021 18:59:08 +0530 Subject: [PATCH 030/856] added discussion hub api routing --- src/proxies_v8/proxies_v8.ts | 2 +- src/utils/proxyCreator.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index a865fdef..7d5049f9 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -240,7 +240,7 @@ proxiesV8.use('/assets/*', // ) proxiesV8.use('/discussion/*', // tslint:disable-next-line: max-line-length - proxyCreatorSunbird(express.Router(), `${CONSTANTS.DISCUSSION_HUB_MIDDLEWARE}`) + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) function removePrefix(prefix: string, s: string) { diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index d4465ab1..eb7954a2 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -20,7 +20,7 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { // condition has been added to set the session in nodebb req header if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create')) { - proxyReq.setHeader('Authorization', 'Bearer ' + req.session.nodebb_authorization_token) + proxyReq.setHeader('nodebb_authorization_token', 'Bearer ' + req.session.nodebb_authorization_token) } if (req.body) { From 6049d3af7152a37709e99853974433019381d9d9 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Fri, 23 Jul 2021 11:32:30 +0530 Subject: [PATCH 031/856] Adding user related API mappings in Proxy --- src/proxies_v8/proxies_v8.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 7d5049f9..91e5b655 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -215,6 +215,10 @@ proxiesV8.use('/org/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) +proxiesV8.use('/user/*', + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + // proxiesV8.use('/api/framework/*', // // tslint:disable-next-line: max-line-length // proxyCreatorQML(express.Router(), `${CONSTANTS.KONG_API_BASE}`, '/api/') From f0f0f44c8ea6fc7003157cee7e63d550d412f1e5 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Fri, 23 Jul 2021 18:30:42 +0530 Subject: [PATCH 032/856] Added User APIs based on SB --- src/protectedApi_v8/user/profile-details.ts | 104 ++++++++++++++------ 1 file changed, 75 insertions(+), 29 deletions(-) diff --git a/src/protectedApi_v8/user/profile-details.ts b/src/protectedApi_v8/user/profile-details.ts index c9ee7966..8d0052c6 100644 --- a/src/protectedApi_v8/user/profile-details.ts +++ b/src/protectedApi_v8/user/profile-details.ts @@ -18,6 +18,12 @@ const API_END_POINTS = { getProfilePageMeta: `${CONSTANTS.USER_PROFILE_API_BASE}/public/v8/profileDetails/getProfilePageMeta`, getUserRegistry: `${CONSTANTS.USER_PROFILE_API_BASE}/public/v8/profileDetails/getUserRegistry`, getUserRegistryById: `${CONSTANTS.USER_PROFILE_API_BASE}/public/v8/profileDetails/getUserRegistryById`, + kongCreateUser: `${CONSTANTS.KONG_API_BASE}/user/v3/create`, + kongSearchUser: `${CONSTANTS.KONG_API_BASE}/user/v1/search`, + kongSendWelcomeEmail: `${CONSTANTS.KONG_API_BASE}/private/user/v1/notification/email`, + kongUpdateUser: `${CONSTANTS.KONG_API_BASE}/user/private/v1/update`, + kongUserRead: (userId: string) => `${CONSTANTS.KONG_API_BASE}/user/v2/read/${userId}`, + kongUserResetPassword: `${CONSTANTS.KONG_API_BASE}/private/user/v1/password/reset`, // tslint:disable-next-line: object-literal-sort-keys migrateRegistry: `${CONSTANTS.USER_PROFILE_API_BASE}/public/v8/profileDetails/migrateRegistry`, resetPassword: `${CONSTANTS.LEARNER_SERVICE_API_BASE}/private/user/v1/password/reset`, @@ -202,6 +208,8 @@ const failedToCreateUser = 'Not able to create User in SunBird' const failedToReadUser = 'Failed to read newly created user details.' const failedToCreateUserInOpenSaber = 'Not able to create User Registry in Opensaber' const createUserFailed = 'ERROR CREATING USER >' +const failedToUpdateUser = 'Failed to update user profile data.' +const unknownError = 'Failed due to unknown reason' profileDeatailsApi.post('/createUser', async (req, res) => { try { @@ -218,8 +226,13 @@ profileDeatailsApi.post('/createUser', async (req, res) => { const searchresponse = await axios({ ...axiosRequestConfig, data: { request: { query: '', filters: { email: sbemail_.toLowerCase() } } }, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), + }, method: 'POST', - url: API_END_POINTS.searchSb, + url: API_END_POINTS.kongSearchUser, }) if (searchresponse.data.result.response.count > 0) { res.status(400).send(emailAdressExist) @@ -238,7 +251,7 @@ profileDeatailsApi.post('/createUser', async (req, res) => { 'x-authenticated-user-token': extractUserToken(req), }, method: 'POST', - url: API_END_POINTS.createSb, + url: API_END_POINTS.kongCreateUser, }) if (response.data.responseCode === 'CLIENT_ERROR') { res.status(400).send(failedToCreateUser) @@ -253,13 +266,41 @@ profileDeatailsApi.post('/createUser', async (req, res) => { 'x-authenticated-user-token': extractUserToken(req), }, method: 'GET', - url: API_END_POINTS.userRead(sbUserId), + url: API_END_POINTS.kongUserRead(sbUserId), }) if (sbUserReadResponse.data.params.status !== 'success') { res.status(500).send(failedToReadUser) return } + const sbProfileUpdateReq = { + profileDetails: { + employmentDetails: { + departmentName: sbChannel, + }, + personalDetails: { + firstname: sbfirstName_, + primaryEmail: sbemail_, + surname: sblastName_, + }, + }, + userId: sbUserId, + } + + const sbUserProfileUpdateResp = await axios({ + ...axiosRequestConfig, + data: { request: sbProfileUpdateReq }, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + }, + method: 'PATCH', + url: API_END_POINTS.kongUpdateUser, + }) + if (sbUserProfileUpdateResp.data.responseCode === 'CLIENT_ERROR') { + res.status(400).send(failedToUpdateUser) + return + } + const passwordResetRequest = { key: 'email', type: 'email', @@ -270,8 +311,11 @@ profileDeatailsApi.post('/createUser', async (req, res) => { const passwordResetResponse = await axios({ ...axiosRequestConfig, data: { request: passwordResetRequest }, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + }, method: 'POST', - url: API_END_POINTS.resetPassword, + url: API_END_POINTS.kongUserResetPassword, }) logInfo('Received response from password reset -> ' + passwordResetResponse) @@ -293,8 +337,11 @@ profileDeatailsApi.post('/createUser', async (req, res) => { const welcomeMailResponse = await axios({ ...axiosRequestConfig, data: { request: welcomeMailRequest }, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + }, method: 'POST', - url: API_END_POINTS.sendWelcomeEmail, + url: API_END_POINTS.kongSendWelcomeEmail, }) if (welcomeMailResponse.data.params.status !== 'success') { @@ -306,31 +353,11 @@ profileDeatailsApi.post('/createUser', async (req, res) => { return } - const personalDetailsRegistry: IPersonalDetails = { - firstname: sbfirstName_, - primaryEmail: sbemail_, - surname: sblastName_, - userName: sbUserReadResponse.data.result.response.userName, - } - const userRegistry = getUserRegistry(personalDetailsRegistry, sbChannel) - const userRegistryResponse = await axios({ - ...axiosRequestConfig, - data: userRegistry, - headers: { - wid: sbUserId, - }, - method: 'POST', - url: API_END_POINTS.createOSUserRegistry(sbUserId), - }) - if (userRegistryResponse.data === null) { - res.status(500).send(failedToCreateUserInOpenSaber) - } else { - const sbUserProfileResponse: Partial = { - email: sbemail_, firstName: sbfirstName_, lastName: sblastName_, - userId: sbUserId, - } - res.send(sbUserProfileResponse) + const sbUserProfileResponse: Partial = { + email: sbemail_, firstName: sbfirstName_, lastName: sblastName_, + userId: sbUserId, } + res.send(sbUserProfileResponse) } } } catch (err) { @@ -339,6 +366,25 @@ profileDeatailsApi.post('/createUser', async (req, res) => { } }) +profileDeatailsApi.patch('/updateUser', async (req, res) => { + try { + const response = await axios.patch(API_END_POINTS.kongUpdateUser, req.body, { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + }, + }) + res.status(response.status).send(response.data) + } catch (err) { + logError(failedToUpdateUser + err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: unknownError, + } + ) + } +}) + profileDeatailsApi.post('/createUserV2WithRegistry', async (req, res) => { try { const sbChannel = req.body.personalDetails.channel From 4a6dd0a0509466f301baf37fdcb43b233314a96a Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Fri, 23 Jul 2021 18:55:59 +0530 Subject: [PATCH 033/856] Fixed compile issue --- src/protectedApi_v8/user/profile-details.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protectedApi_v8/user/profile-details.ts b/src/protectedApi_v8/user/profile-details.ts index 8d0052c6..c6bda720 100644 --- a/src/protectedApi_v8/user/profile-details.ts +++ b/src/protectedApi_v8/user/profile-details.ts @@ -226,7 +226,7 @@ profileDeatailsApi.post('/createUser', async (req, res) => { const searchresponse = await axios({ ...axiosRequestConfig, data: { request: { query: '', filters: { email: sbemail_.toLowerCase() } } }, - headers: { + headers: { Authorization: CONSTANTS.SB_API_KEY, // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req), From 1245ef8ee84916e1588a8097c23d142a19fe4b20 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Mon, 26 Jul 2021 13:30:46 +0530 Subject: [PATCH 034/856] Changes for cors and reset --- src/server.ts | 6 ++++- src/utils/apiWhiteList.ts | 49 +++++++++++++++++++++------------------ src/utils/env.ts | 1 + 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/server.ts b/src/server.ts index 08291078..caa47075 100644 --- a/src/server.ts +++ b/src/server.ts @@ -39,7 +39,11 @@ export class Server { protected app = express() private keycloak?: CustomKeycloak private constructor() { - this.app.use(cors({origin: 'https://local.igot-dev.in:3000', credentials: true})) + if (CONSTANTS.CORS_ENVIRONMENT === 'dev') { + this.app.use(cors({origin: 'https://local.igot-dev.in:3000', credentials: true})) + } else { + this.app.use(cors()) + } const sessionConfig = getSessionConfig() this.app.use(expressSession(sessionConfig)) this.app.all('*', apiWhiteListLogger()) diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index 72ffe17f..fd2d2afb 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -137,29 +137,34 @@ const respond403 = (req: Request, res: Response) => { const respond419 = (req: Request, res: Response) => { const REQ_URL = req.path - const err = ({ msg: 'API WHITELIST :: Unauthorized access for API [ ' + REQ_URL + ' ]', url: REQ_URL }) - logError(err.msg) - res.status(419) - res.setHeader('location', redirectToLogin(req)) - res.send( - { - id: 'api.error', - ver: '1.0', - // tslint:disable-next-line: object-literal-sort-keys - ts: dateFormat(new Date(), 'yyyy-mm-dd HH:MM:ss:lo'), - params: + if (_.includes(REQ_URL, '/reset')) { + res.send('You are logged out!') + } else { + const err = ({ msg: 'API WHITELIST :: Unauthorized access for API [ ' + REQ_URL + ' ]', url: REQ_URL }) + logError(err.msg) + res.status(419) + res.setHeader('location', redirectToLogin(req)) + res.send( { - resmsgid: uuidv1(), + id: 'api.error', + ver: '1.0', // tslint:disable-next-line: object-literal-sort-keys - msgid: null, - status: 'failed', - err: 'UNAUTHORIZED_ERROR', - errmsg: 'UNAUTHORIZED: Access is denied', - }, - responseCode: 'UNAUTHORIZED', - redirectUrl: redirectToLogin(req), - result: {}, - }) + ts: dateFormat(new Date(), 'yyyy-mm-dd HH:MM:ss:lo'), + params: + { + resmsgid: uuidv1(), + // tslint:disable-next-line: object-literal-sort-keys + msgid: null, + status: 'failed', + err: 'UNAUTHORIZED_ERROR', + errmsg: 'UNAUTHORIZED: Access is denied', + }, + responseCode: 'UNAUTHORIZED', + redirectUrl: redirectToLogin(req), + result: {}, + }) + } + res.end() } @@ -174,7 +179,7 @@ const respond419 = (req: Request, res: Response) => { export const isAllowed = () => { // tslint:disable-next-line: only-arrow-functions return function(req: Request, res: Response, next: NextFunction) { - if (CONSTANTS.PORTAL_API_WHITELIST_CHECK) { + if (CONSTANTS.PORTAL_API_WHITELIST_CHECK === 'true') { if (shouldAllow(req)) { next() } else { diff --git a/src/utils/env.ts b/src/utils/env.ts index ab7d7f2f..e39ddfa8 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -19,6 +19,7 @@ export const CONSTANTS = { CONTENT_HIERARCHY: env.CONTENT_HIERARCHY || 'http://localhost:5903/hierarchy', CONTENT_META_FETCH_API_BASE: env.CONTENT_META_FETCH_API_BASE || 'http://localhost:5906', CONTENT_STORE_DEVELOPMENT_BASE: '', + CORS_ENVIRONMENT: env.CORS_ENVIRONMENT || 'dev', COUNTER: 'http://localhost:5903', DEFAULT_ORG: env.DEFAULT_ORG || 'dopt', DEFAULT_ROOT_ORG: env.DEFAULT_ROOT_ORG || 'igot', From 1c249596a45796c06b65408d307a33da13a92400 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Mon, 26 Jul 2021 15:25:46 +0530 Subject: [PATCH 035/856] Changes for env --- src/utils/env.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/env.ts b/src/utils/env.ts index e39ddfa8..66345cb5 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -19,7 +19,7 @@ export const CONSTANTS = { CONTENT_HIERARCHY: env.CONTENT_HIERARCHY || 'http://localhost:5903/hierarchy', CONTENT_META_FETCH_API_BASE: env.CONTENT_META_FETCH_API_BASE || 'http://localhost:5906', CONTENT_STORE_DEVELOPMENT_BASE: '', - CORS_ENVIRONMENT: env.CORS_ENVIRONMENT || 'dev', + CORS_ENVIRONMENT: env.CORS_ENVIRONMENT || 'prod', COUNTER: 'http://localhost:5903', DEFAULT_ORG: env.DEFAULT_ORG || 'dopt', DEFAULT_ROOT_ORG: env.DEFAULT_ROOT_ORG || 'igot', From 10ffe79e22b566da476888ff926e64dd7dcf9531 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Mon, 26 Jul 2021 16:16:08 +0530 Subject: [PATCH 036/856] Changes for cluster --- src/index.ts | 6 +++--- src/utils/env.ts | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/index.ts b/src/index.ts index 7bbb9f87..3f2b36a6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,4 @@ import cluster from 'cluster' -import * as os from 'os' import { Server } from './server' // Code to inject axios retry logic @@ -13,9 +12,10 @@ if (cluster.isMaster) { if (CONSTANTS.IS_DEVELOPMENT) { cluster.fork() } else { - os.cpus().forEach(() => { + const threadCount = CONSTANTS.CLUSTER_THREAD + for (let index = 0; index < threadCount; index++) { cluster.fork() - }) + } } cluster.on('exit', () => { diff --git a/src/utils/env.ts b/src/utils/env.ts index 66345cb5..6f692ec7 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -15,6 +15,7 @@ export const CONSTANTS = { CASSANDRA_KEYSPACE: env.CASSANDRA_KEYSPACE || 'bodhi', CASSANDRA_PASSWORD: env.CASSANDRA_PASSWORD || '', CASSANDRA_USERNAME: env.CASSANDRA_USERNAME || '', + CLUSTER_THREAD: env.CLUSTER_THREAD || 1, CONTENT_API_BASE: env.CONTENT_API_BASE || 'http://localhost:5903', CONTENT_HIERARCHY: env.CONTENT_HIERARCHY || 'http://localhost:5903/hierarchy', CONTENT_META_FETCH_API_BASE: env.CONTENT_META_FETCH_API_BASE || 'http://localhost:5906', From fb8d13441330d0877bf35f9a418d080623c15816 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Tue, 27 Jul 2021 18:13:20 +0530 Subject: [PATCH 037/856] Added hierarchy update api --- src/proxies_v8/proxies_v8.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 91e5b655..7cb52b54 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -197,6 +197,9 @@ proxiesV8.use([ proxiesV8.use('/action/content/v3/updateReviewStatus', proxyCreatorKnowledge(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) +proxiesV8.use('/action/content/v3/hierarchyUpdate', + proxyCreatorKnowledge(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) proxiesV8.use('/action/*', proxyCreatorKnowledge(express.Router(), `${CONSTANTS.KNOWLEDGE_MW_API_BASE}`) ) From c07ec183a4013bb30f122182fbb53417b533bead Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Tue, 27 Jul 2021 22:42:39 +0530 Subject: [PATCH 038/856] Task55145-Changes for whitelist --- src/server.ts | 5 +- src/utils/apiWhiteList.ts | 27 +- src/utils/permissionHelper.ts | 4 + src/utils/whitelistApis.ts | 730 +++++++++++++++++++++++++++++++++- 4 files changed, 752 insertions(+), 14 deletions(-) diff --git a/src/server.ts b/src/server.ts index caa47075..8940218f 100644 --- a/src/server.ts +++ b/src/server.ts @@ -21,7 +21,7 @@ import { logInfo, logSuccess } from './utils/logger' const cookieParser = require('cookie-parser') const healthcheck = require('express-healthcheck') -import { apiWhiteListLogger } from './utils/apiWhiteList' +import { apiWhiteListLogger, isAllowed } from './utils/apiWhiteList' function haltOnTimedOut(req: Express.Request, _: Express.Response, next: NextFunction) { if (!req.timedout) { @@ -47,6 +47,9 @@ export class Server { const sessionConfig = getSessionConfig() this.app.use(expressSession(sessionConfig)) this.app.all('*', apiWhiteListLogger()) + if (CONSTANTS.PORTAL_API_WHITELIST_CHECK === 'true') { + this.app.all('*', isAllowed()) + } this.setCookie() this.setKeyCloak(sessionConfig) this.authoringProxies() diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index fd2d2afb..e44432ec 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -179,11 +179,12 @@ const respond419 = (req: Request, res: Response) => { export const isAllowed = () => { // tslint:disable-next-line: only-arrow-functions return function(req: Request, res: Response, next: NextFunction) { + let REQ_URL = req.path if (CONSTANTS.PORTAL_API_WHITELIST_CHECK === 'true') { - if (shouldAllow(req)) { + if (shouldAllow(req) || _.includes(REQ_URL, '/resource')) { next() } else { - let REQ_URL = req.path + // Pattern match for URL // tslint:disable-next-line: no-any _.forEach(API_LIST.URL_PATTERN, (url: any) => { @@ -231,6 +232,26 @@ const redirectToLogin = (req: Request) => { const redirectUrl = 'protected/v8/resource/' return `https://${req.get('host')}/${redirectUrl}` // 'http://localhost:3003/protected/v8/user/resource/' } + +const validateAPI = (req: Request, res: Response, next: NextFunction) => { + let REQ_URL_ORIGINAL = req.path + // tslint:disable-next-line: no-any + _.forEach(API_LIST.URL_PATTERN, (url: any) => { + const regExp = pathToRegexp(url) + if (regExp.test(REQ_URL_ORIGINAL)) { + REQ_URL_ORIGINAL = url + return false + } + return true + }) + if (_.get(API_LIST.URL, REQ_URL_ORIGINAL)) { + next() + } else { + // If API is not whitelisted + logInfo('Portal_API_WHITELIST_LOGGER: URL not whitelisted') + respond403(req, res) + } +} /** * This function is used for checking whether */ @@ -248,8 +269,8 @@ export function apiWhiteListLogger() { respond419(req, res) } else { // Pattern match for URL - next() logInfo('In WhilteList Call========' + REQ_URL) + validateAPI(req, res, next) } } else { next() diff --git a/src/utils/permissionHelper.ts b/src/utils/permissionHelper.ts index a76af67d..132f3c8f 100644 --- a/src/utils/permissionHelper.ts +++ b/src/utils/permissionHelper.ts @@ -1,3 +1,4 @@ +const _ = require('lodash') import request from 'request' import { CONSTANTS } from './env' @@ -10,6 +11,9 @@ export const PERMISSION_HELPER = { reqObj.session.userId = userData.result.response.id ? userData.result.response.id : userData.result.response.userId reqObj.session.userName = userData.result.response.userName reqObj.session.userRoles = userData.result.response.roles + if (!_.includes(reqObj.session.userRoles, 'PUBLIC')) { + reqObj.session.userRoles.push('PUBLIC') + } // tslint:disable-next-line: no-any reqObj.session.save((error: any) => { if (error) { diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index e526b14f..e9795b91 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -5,28 +5,34 @@ const CHECK = { // Valid Roles const ROLE = { - CONTENT_CREATOR: 'CONTENT_CREATOR', - // tslint:disable-next-line: object-literal-sort-keys COMPENTENCY_REVIEWER: 'competencyReviewer', + CONTENT_CREATOR: 'CONTENT_CREATOR', EDITOR: 'EDITOR', - IFU_MEMBER: 'IFUMember', - MDO_ADMIN: 'MDO ADMIN', - MEMBER: 'Member', - PUBLISHER: 'PUBLISHER', - REVIEWER: 'REVIEWER', FRAC_ACCESS_COMPENTENCY: 'fracAccessCompetency', FRAC_ADMIN: 'fracAdmin', FRAC_REVIEWER_ONE: 'fracReviewerOne', FRAC_REVIEWER_TWO: 'fracReviewerTwo', + IFU_MEMBER: 'IFUMember', + MDO_ADMIN: 'MDO ADMIN', + MEMBER: 'Member', PUBLIC: 'PUBLIC', + PUBLISHER: 'PUBLISHER', + REVIEWER: 'REVIEWER', } // All api list validations export const API_LIST = { URL: { + '/authApi/content/v3/create': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/user/details': { - checksNeeded: [], + checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, @@ -42,14 +48,630 @@ export const API_LIST = { }, // tslint:disable-next-line: object-literal-sort-keys '/protected/v8/resource/': { - checksNeeded: [], + checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, '/proxies/v8/api/user/v2/read': { - checksNeeded: [], + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/user/v1/read/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/sunbirdigot/read': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/read/content-progres/:do_id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/discussion/recent': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/content-progres/:do_id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/discussion/user/v1/create': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/discussion/topic/:id/:slug': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/learner/course/v1/user/enrollment/list/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/action/content/v3/hierarchy/:do_id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/action/content/v3/read/:do_id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/discussion/forum/v2/read': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/discussion/forum/v3/create': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/discussion/category/list': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/discussion/category/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/discussion/forum/tags': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/discussion/user/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/action/content/v3/hierarchyUpdate': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/action/content/v3/hierarchy/update': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/action/content/v3/create': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/upload/action/content/v3/upload/:do_id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/v1/content/retire': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/action/content/v3/updateReviewStatus/': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/action/content/v3/review/:do_id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/action/content/v3/publish/': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/action/content/v3/update/:do_id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/connections/connections/recommended/userDepartment': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/discussionHub/categories/:cid/:slug?/:tid?': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/discussionHub/topics/recent': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/cohorts/:cohortType/:contentId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/user/content/like': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/user/telemetry': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/scrom/get/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/user/progress/:contentId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/user/rating/:contentId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/user/progress': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/user/history/continue': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/portal/departmentType/': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/portal/spv/department': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/portal/spv/deptAction/': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/user/roles/getUsersV2': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/portal/spv/mydepartment': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/portal/mdo/mydepartment': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/user/profileDetails/createUser': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/user/profileRegistry/getUserRegistryByUser/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/user/profileRegistry/createUserRegistryV2': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/portal/deptAction': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/workflowhandler/historyByApplicationId/': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/user/autocomplete/:query': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/portal/spv/deptAction/userrole': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/user/preference': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/workflowhandler/applicationsSearch': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/workallocation/getWorkOrders': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/workallocation/add/workorder': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/workallocation/getWorkOrderById/:workOrderId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/workallocation/user/autocomplete/:searchTerm': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/frac/searchNodes': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/frac/:type/:key': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/workallocation/v2/add': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/workallocation/getWorkAllocationById/:workAllocationId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/workallocation/update/workorder': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/workallocation/getWOPdf/:workOrderId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/portal/cbp/mydepartment': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/user/mandatoryContent/checkStatus': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/user/rating/content/average-ratingInfo/:do_id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/social/post/timeline': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/user/history/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/frac/getAllNodes/:type': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/frac/getNodeById/:id/:type': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/portal/listDeptNames': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/scroing/getTemplate/:templateId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/portal/cbc/department': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/portal/cbc/department/:deptId/': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/portal/spv/department/:deptId/': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/apis/protected/v8/scroing/calculate': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/connections/connections/recommended': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/connections/connections/requests/received': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/connections/connections/established': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/connections/connections/established/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/connections/connections/requested': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/connections/add/connection': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/connections/connections/suggests': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/cohorts/user/autoenrollment/:courseId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/profanity/startPdfProfanity': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/profanity/getPdfProfanityForContent/:contentId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/catalog': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/scroing/fetch': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/portal/mdo/deptAction/userrole': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/workallocation/v2/update': { + checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, @@ -65,10 +687,98 @@ export const API_LIST = { }, URL_PATTERN: [ + '/authApi/content/v3/create', '/proxies/v8/api/user/v2/read', + '/proxies/v8/user/v1/read/:id', + '/proxies/v8/sunbirdigot/read', + '/proxies/v8/discussion/user/v1/create', + '/proxies/v8/discussion/topic/:id/:slug', + '/proxies/v8/learner/course/v1/user/enrollment/list/:id', + '/proxies/v8/read/content-progres/:do_id', + '/proxies/v8/content-progres/:do_id', + '/proxies/v8/action/content/v3/hierarchy/:do_id', + '/proxies/v8/action/content/v3/hierarchyUpdate', + '/proxies/v8/action/content/v3/hierarchy/update', + '/proxies/v8/action/content/v3/read/:do_id', + '/proxies/v8/discussion/forum/v2/read', + '/proxies/v8/discussion/category/list', + '/proxies/v8/discussion/category/:id', + '/proxies/v8/discussion/forum/tags', + '/proxies/v8/action/content/v3/update/:do_id', + '/proxies/v8/discussion/user/:id', + '/proxies/v8/action/content/v3/create', + '/proxies/v8/upload/action/content/v3/upload/:do_id', + '/proxies/v8/v1/content/retire', + '/proxies/v8/action/content/v3/updateReviewStatus/', + '/proxies/v8/action/content/v3/review/:do_id', + '/proxies/v8/discussion/recent', + '/proxies/v8/action/content/v3/publish/', '/protected/v8/user/profileDetails/test', '/protected/v8/resource/', '/protected/v8/user/details', + '/protected/v8/connections/connections/recommended/userDepartment', + '/protected/v8/discussionHub/categories/:cid/:slug?/:tid?', + '/protected/v8/discussionHub/topics/recent', + '/protected/v8/cohorts/:cohortType/:contentId', + '/protected/v8/user/content/like', + '/protected/v8/user/telemetry', + '/protected/v8/scrom/get/:id', + '/protected/v8/user/progress/:contentId', + '/protected/v8/user/rating/:contentId', + '/protected/v8/user/progress', + '/protected/v8/user/history/continue', + '/protected/v8/portal/departmentType/', + '/protected/v8/portal/spv/department', + '/protected/v8/portal/spv/deptAction/', + '/protected/v8/user/roles/getUsersV2', + '/protected/v8/portal/spv/mydepartment', + '/protected/v8/portal/mdo/mydepartment', + '/protected/v8/user/profileDetails/createUser', + '/protected/v8/user/profileRegistry/getUserRegistryByUser/:id', + '/protected/v8/user/profileRegistry/createUserRegistryV2', + '/protected/v8/portal/deptAction', + '/protected/v8/workflowhandler/historyByApplicationId/', + '/protected/v8/user/autocomplete/:query', + '/protected/v8/portal/spv/deptAction/userrole', + '/protected/v8/user/preference', + '/protected/v8/workflowhandler/applicationsSearch', + '/protected/v8/workallocation/getWorkOrders', + '/protected/v8/workallocation/add/workorder', + '/protected/v8/workallocation/getWorkOrderById/:workOrderId', + '/protected/v8/workallocation/user/autocomplete/:searchTerm', + '/protected/v8/frac/searchNodes', + '/protected/v8/frac/:type/:key', + '/protected/v8/workallocation/v2/add', + '/protected/v8/workallocation/getWorkAllocationById/:workAllocationId', + '/protected/v8/workallocation/update/workorder', + '/protected/v8/workallocation/v2/update', + '/protected/v8/workallocation/getWOPdf/:workOrderId', + '/protected/v8/portal/cbp/mydepartment', + '/protected/v8/user/mandatoryContent/checkStatus', + '/protected/v8/user/rating/content/average-ratingInfo/:do_id', + '/protected/v8/social/post/timeline', + '/protected/v8/user/history/:id', + '/protected/v8/frac/getAllNodes/:type', + '/protected/v8/frac/getNodeById/:id/:type', + '/protected/v8/portal/listDeptNames', + '/protected/v8/scroing/getTemplate/:templateId', + '/protected/v8/portal/cbc/department', + '/protected/v8/portal/cbc/department/:deptId/', + '/protected/v8/portal/spv/department/:deptId/', + '/protected/v8/scroing/calculate', + '/protected/v8/connections/connections/recommended', + '/protected/v8/connections/connections/requests/received', + '/protected/v8/connections/connections/established', + '/protected/v8/connections/connections/established/:id', + '/protected/v8/connections/connections/requested', + '/protected/v8/connections/add/connection', + '/protected/v8/connections/connections/suggests', + '/protected/v8/cohorts/user/autoenrollment/:courseId', + '/protected/v8/profanity/startPdfProfanity', + '/protected/v8/profanity/getPdfProfanityForContent/:contentId', + '/protected/v8/catalog', + '/protected/v8/scroing/fetch', + '/protected/v8/portal/mdo/deptAction/userrole', '/reset', ], } From 65b425e02357e3d2a2c704dcd3401557e67bae47 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Wed, 28 Jul 2021 15:33:33 +0530 Subject: [PATCH 039/856] Changes for whitelist apis --- src/utils/proxyCreator.ts | 40 +++++++++++++-------------- src/utils/whitelistApis.ts | 56 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 20 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index eb7954a2..dcfee61d 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -35,26 +35,26 @@ proxy.on('proxyRes', (proxyRes: any, req: any, _res: any, ) => { // res.removeHeader('access-control-allow-origin') delete proxyRes.headers['access-control-allow-origin'] // write user session with roles - if (req.originalUrl.includes('/user/v2/read')) { - // tslint:disable-next-line: no-any - proxyRes.on('data', (data: any) => { - if ((proxyRes.statusCode === 200 || proxyRes.statusCode === 201)) { - data = JSON.parse(data.toString('utf-8')) - const roles = data.result.response.roles - req.session.userId = data.result.response.id ? data.result.response.id : data.result.response.userId - req.session.userName = data.result.response.userName - req.session.userRoles = roles - // console.log(req); - // tslint:disable-next-line: only-arrow-functions - req.session.save(function(error: string) { - if (error) { - // tslint:disable-next-line: no-console - console.log(error) - } - }) - } - }) - } + // if (req.originalUrl.includes('/user/v2/read')) { + // // tslint:disable-next-line: no-any + // proxyRes.on('data', (data: any) => { + // if ((proxyRes.statusCode === 200 || proxyRes.statusCode === 201)) { + // data = JSON.parse(data.toString('utf-8')) + // const roles = data.result.response.roles + // req.session.userId = data.result.response.id ? data.result.response.id : data.result.response.userId + // req.session.userName = data.result.response.userName + // req.session.userRoles = roles + // // console.log(req); + // // tslint:disable-next-line: only-arrow-functions + // req.session.save(function(error: string) { + // if (error) { + // // tslint:disable-next-line: no-console + // console.log(error) + // } + // }) + // } + // }) + // } if (req.originalUrl.includes('/discussion/user/v1/create')) { const nodebb_auth_token = proxyRes.headers.nodebb_auth_token diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index e9795b91..54281d45 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -236,6 +236,48 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/data/v1/system/settings/get/orgTypeList': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/org/v1/search': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/user/private/v1/assign/role': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/user/v1/search': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/org/v1/read': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/org/v1/update': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/connections/connections/recommended/userDepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -677,6 +719,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/user/profileDetails/updateUser': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/reset': { checksNeeded: [], // tslint:disable-next-line: object-literal-sort-keys @@ -692,6 +741,9 @@ export const API_LIST = { '/proxies/v8/user/v1/read/:id', '/proxies/v8/sunbirdigot/read', '/proxies/v8/discussion/user/v1/create', + '/proxies/v8/data/v1/system/settings/get/orgTypeList', + '/proxies/v8/org/v1/search', + '/proxies/v8/org/v1/update', '/proxies/v8/discussion/topic/:id/:slug', '/proxies/v8/learner/course/v1/user/enrollment/list/:id', '/proxies/v8/read/content-progres/:do_id', @@ -713,6 +765,9 @@ export const API_LIST = { '/proxies/v8/action/content/v3/review/:do_id', '/proxies/v8/discussion/recent', '/proxies/v8/action/content/v3/publish/', + '/proxies/v8/user/private/v1/assign/role', + '/proxies/v8/user/v1/search', + '/proxies/v8/org/v1/read', '/protected/v8/user/profileDetails/test', '/protected/v8/resource/', '/protected/v8/user/details', @@ -779,6 +834,7 @@ export const API_LIST = { '/protected/v8/catalog', '/protected/v8/scroing/fetch', '/protected/v8/portal/mdo/deptAction/userrole', + '/protected/v8/user/profileDetails/updateUser', '/reset', ], } From 98b6af348fa1013538967567949dd00133bf39e3 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Wed, 28 Jul 2021 16:40:51 +0530 Subject: [PATCH 040/856] changes for whitelist --- src/utils/whitelistApis.ts | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 54281d45..5543d10a 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -278,6 +278,27 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/user/v1/block': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/user/v1/unblock': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/data/v1/system/settings/get/orgTypeConfig': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/connections/connections/recommended/userDepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -614,7 +635,7 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/apis/protected/v8/scroing/calculate': { + '/protected/v8/scroing/calculate': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -768,6 +789,10 @@ export const API_LIST = { '/proxies/v8/user/private/v1/assign/role', '/proxies/v8/user/v1/search', '/proxies/v8/org/v1/read', + '/proxies/v8/discussion/tags', + '/proxies/v8/user/v1/block', + '/proxies/v8/user/v1/unblock', + '/proxies/v8/data/v1/system/settings/get/orgTypeConfig', '/protected/v8/user/profileDetails/test', '/protected/v8/resource/', '/protected/v8/user/details', From b558bd5e617068115451c59622a5e4d183dc0b4e Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Wed, 28 Jul 2021 18:18:45 +0530 Subject: [PATCH 041/856] Add apis for whitelist --- src/utils/whitelistApis.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 5543d10a..34249f5c 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -31,6 +31,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/authApi/content/v3/read/:do_id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/user/details': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -299,6 +306,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/discussion/tags': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/connections/connections/recommended/userDepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -747,6 +761,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/frac/addDataNodeBulk': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/reset': { checksNeeded: [], // tslint:disable-next-line: object-literal-sort-keys @@ -758,6 +779,7 @@ export const API_LIST = { URL_PATTERN: [ '/authApi/content/v3/create', + '/authApi/content/v3/read/:do_id', '/proxies/v8/api/user/v2/read', '/proxies/v8/user/v1/read/:id', '/proxies/v8/sunbirdigot/read', @@ -860,6 +882,7 @@ export const API_LIST = { '/protected/v8/scroing/fetch', '/protected/v8/portal/mdo/deptAction/userrole', '/protected/v8/user/profileDetails/updateUser', + '/protected/v8/frac/addDataNodeBulk', '/reset', ], } From 5b5f2c30c8e3732121bb9569cd91d6f818d5dcbf Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Wed, 28 Jul 2021 19:12:26 +0530 Subject: [PATCH 042/856] Added api to whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 34249f5c..3c2b7e79 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -768,6 +768,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/roleactivity': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/reset': { checksNeeded: [], // tslint:disable-next-line: object-literal-sort-keys @@ -883,6 +890,7 @@ export const API_LIST = { '/protected/v8/portal/mdo/deptAction/userrole', '/protected/v8/user/profileDetails/updateUser', '/protected/v8/frac/addDataNodeBulk', + '/protected/v8/roleactivity', '/reset', ], } From 78b1a43e60600b636b92e5f00ddc9dd8cc4f4699 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Wed, 28 Jul 2021 20:04:26 +0530 Subject: [PATCH 043/856] Changes for read api and whitelist --- src/utils/proxyCreator.ts | 11 ++++++++--- src/utils/whitelistApis.ts | 12 ++++++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index dcfee61d..a52b5181 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -181,15 +181,20 @@ export function proxyCreatorSunbirdSearch(route: Router, targetUrl: string, _tim export function proxyCreatorToAppentUserId(route: Router, targetUrl: string, _timeout = 10000): Router { route.all('/*', (req, res) => { - const userId = extractUserIdFromRequest(req).split(':') - + const originalUrl = req.originalUrl + const lastIndex = originalUrl.lastIndexOf('/') + const subStr = originalUrl.substr(lastIndex).substr(1).split('-').length + let userId = extractUserIdFromRequest(req).split(':')[2] + if (subStr === 5 && (originalUrl.substr(lastIndex).substr(1))) { + userId = originalUrl.substr(lastIndex).substr(1) + } // tslint:disable-next-line: no-console console.log('REQ_URL_ORIGINAL proxyCreatorToAppentUserId', req.originalUrl) proxy.web(req, res, { changeOrigin: true, ignorePath: true, - target: targetUrl + userId[userId.length - 1], + target: targetUrl + userId, // [userId.length - 1], }) }) return route diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 3c2b7e79..c89ce69e 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -68,6 +68,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/api/user/v2/read/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/user/v1/read/:id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -460,7 +467,7 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/protected/v8/workflowhandler/historyByApplicationId/': { + '/protected/v8/workflowhandler/historyByApplicationId/:applicationId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -788,6 +795,7 @@ export const API_LIST = { '/authApi/content/v3/create', '/authApi/content/v3/read/:do_id', '/proxies/v8/api/user/v2/read', + '/proxies/v8/api/user/v2/read/:id', '/proxies/v8/user/v1/read/:id', '/proxies/v8/sunbirdigot/read', '/proxies/v8/discussion/user/v1/create', @@ -846,7 +854,7 @@ export const API_LIST = { '/protected/v8/user/profileRegistry/getUserRegistryByUser/:id', '/protected/v8/user/profileRegistry/createUserRegistryV2', '/protected/v8/portal/deptAction', - '/protected/v8/workflowhandler/historyByApplicationId/', + '/protected/v8/workflowhandler/historyByApplicationId/:applicationId', '/protected/v8/user/autocomplete/:query', '/protected/v8/portal/spv/deptAction/userrole', '/protected/v8/user/preference', From 12b9a9d9a574eabc194d09d39dbfa0425f9e85b3 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Thu, 29 Jul 2021 10:22:22 +0530 Subject: [PATCH 044/856] added api to whitelist --- src/utils/whitelistApis.ts | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index c89ce69e..9c1032d0 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -89,6 +89,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/sunbirdigot/search': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/read/content-progres/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -222,7 +229,7 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/proxies/v8/action/content/v3/updateReviewStatus/': { + '/proxies/v8/action/content/v3/updateReviewStatus/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -320,6 +327,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/org/v1/create': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/connections/connections/recommended/userDepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -798,6 +812,7 @@ export const API_LIST = { '/proxies/v8/api/user/v2/read/:id', '/proxies/v8/user/v1/read/:id', '/proxies/v8/sunbirdigot/read', + '/proxies/v8/sunbirdigot/search', '/proxies/v8/discussion/user/v1/create', '/proxies/v8/data/v1/system/settings/get/orgTypeList', '/proxies/v8/org/v1/search', @@ -819,13 +834,14 @@ export const API_LIST = { '/proxies/v8/action/content/v3/create', '/proxies/v8/upload/action/content/v3/upload/:do_id', '/proxies/v8/v1/content/retire', - '/proxies/v8/action/content/v3/updateReviewStatus/', + '/proxies/v8/action/content/v3/updateReviewStatus/:do_id', '/proxies/v8/action/content/v3/review/:do_id', '/proxies/v8/discussion/recent', '/proxies/v8/action/content/v3/publish/', '/proxies/v8/user/private/v1/assign/role', '/proxies/v8/user/v1/search', '/proxies/v8/org/v1/read', + '/proxies/v8/org/v1/create', '/proxies/v8/discussion/tags', '/proxies/v8/user/v1/block', '/proxies/v8/user/v1/unblock', From 8005e40ec053507d99939bfc3beb16553e9ffb41 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Thu, 29 Jul 2021 11:10:37 +0530 Subject: [PATCH 045/856] added whitelist --- src/utils/whitelistApis.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 9c1032d0..affba264 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -243,7 +243,7 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/proxies/v8/action/content/v3/publish/': { + '/proxies/v8/action/content/v3/publish/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -837,7 +837,7 @@ export const API_LIST = { '/proxies/v8/action/content/v3/updateReviewStatus/:do_id', '/proxies/v8/action/content/v3/review/:do_id', '/proxies/v8/discussion/recent', - '/proxies/v8/action/content/v3/publish/', + '/proxies/v8/action/content/v3/publish/:do_id', '/proxies/v8/user/private/v1/assign/role', '/proxies/v8/user/v1/search', '/proxies/v8/org/v1/read', From 92c64b6dac8c187ec76eeda525175ab7047798bf Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Thu, 29 Jul 2021 11:52:40 +0530 Subject: [PATCH 046/856] Add api to whitelist --- src/utils/whitelistApis.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index affba264..81baa276 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -334,6 +334,20 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/learnervm/private/content/v3/publish/:do_id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/learnervm/private/content/v3/review/:do_id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/connections/connections/recommended/userDepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -846,6 +860,8 @@ export const API_LIST = { '/proxies/v8/user/v1/block', '/proxies/v8/user/v1/unblock', '/proxies/v8/data/v1/system/settings/get/orgTypeConfig', + '/proxies/v8/learnervm/private/content/v3/publish/:do_id', + '/proxies/v8/learnervm/private/content/v3/review/:do_id', '/protected/v8/user/profileDetails/test', '/protected/v8/resource/', '/protected/v8/user/details', From eb7109294963ff1ec30b35b411993052f62b4575 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Thu, 29 Jul 2021 14:23:02 +0530 Subject: [PATCH 047/856] Added api to whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 81baa276..9161835b 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -810,6 +810,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/connections/update/connection': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/reset': { checksNeeded: [], // tslint:disable-next-line: object-literal-sort-keys @@ -931,6 +938,7 @@ export const API_LIST = { '/protected/v8/user/profileDetails/updateUser', '/protected/v8/frac/addDataNodeBulk', '/protected/v8/roleactivity', + '/protected/v8/connections/update/connection', '/reset', ], } From be670672059fef1b2f1c520bfe8311586517856a Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Thu, 29 Jul 2021 15:32:54 +0530 Subject: [PATCH 048/856] Added new apis to whitelist --- src/utils/whitelistApis.ts | 56 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 9161835b..78aa7e05 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -348,6 +348,55 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/discussion/v2/topics': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/discussion/tags/': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/discussion/user/:userKey/bookmarks': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/discussion/user/:userKey/upvoted': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/discussion/user/:userKey/downvoted': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/discussion/categories': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/user/v1/autocomplete/:key': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/connections/connections/recommended/userDepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -869,6 +918,13 @@ export const API_LIST = { '/proxies/v8/data/v1/system/settings/get/orgTypeConfig', '/proxies/v8/learnervm/private/content/v3/publish/:do_id', '/proxies/v8/learnervm/private/content/v3/review/:do_id', + '/proxies/v8/discussion/v2/topics', + '/proxies/v8/discussion/tags/', + '/proxies/v8/discussion/user/:userKey/bookmarks', + '/proxies/v8/discussion/user/:userKey/upvoted', + '/proxies/v8/discussion/user/:userKey/downvoted', + '/proxies/v8/discussion/categories', + '/proxies/v8/user/v1/autocomplete/:key', '/protected/v8/user/profileDetails/test', '/protected/v8/resource/', '/protected/v8/user/details', From 6d6c19a3f222e8d8e449b12f07b44941077deb5e Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Thu, 29 Jul 2021 15:40:32 +0530 Subject: [PATCH 049/856] Added new apis --- src/utils/whitelistApis.ts | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 78aa7e05..ebd557e1 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -866,6 +866,41 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/workflowhandler/userWFApplicationFieldsSearch': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/user/details/detailV1': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/user/profileRegistry/getMasterNationalities': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/user/profileRegistry/getMasterLanguages': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/user/profileRegistry/getProfilePageMeta': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/reset': { checksNeeded: [], // tslint:disable-next-line: object-literal-sort-keys @@ -995,6 +1030,11 @@ export const API_LIST = { '/protected/v8/frac/addDataNodeBulk', '/protected/v8/roleactivity', '/protected/v8/connections/update/connection', + '/protected/v8/workflowhandler/userWFApplicationFieldsSearch', + '/protected/v8/user/details/detailV1', + '/protected/v8/user/profileRegistry/getMasterNationalities', + '/protected/v8/user/profileRegistry/getMasterLanguages', + '/protected/v8/user/profileRegistry/getProfilePageMeta', '/reset', ], } From ebe7e166294792fdbf92cfabc2d91d068314cabf Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Thu, 29 Jul 2021 18:32:42 +0530 Subject: [PATCH 050/856] Added api to whitelist --- src/utils/whitelistApis.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index ebd557e1..29d57a05 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -537,6 +537,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/user/profileRegistry/createUserRegistry': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/portal/deptAction': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -901,6 +908,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/user/notifications/settings': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/reset': { checksNeeded: [], // tslint:disable-next-line: object-literal-sort-keys @@ -983,6 +997,7 @@ export const API_LIST = { '/protected/v8/user/profileDetails/createUser', '/protected/v8/user/profileRegistry/getUserRegistryByUser/:id', '/protected/v8/user/profileRegistry/createUserRegistryV2', + '/protected/v8/user/profileRegistry/createUserRegistry', '/protected/v8/portal/deptAction', '/protected/v8/workflowhandler/historyByApplicationId/:applicationId', '/protected/v8/user/autocomplete/:query', @@ -1035,6 +1050,7 @@ export const API_LIST = { '/protected/v8/user/profileRegistry/getMasterNationalities', '/protected/v8/user/profileRegistry/getMasterLanguages', '/protected/v8/user/profileRegistry/getProfilePageMeta', + '/protected/v8/user/notifications/settings', '/reset', ], } From 4c48815ad66a0600941f7c3cfc6152fad5033f99 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Thu, 29 Jul 2021 18:32:42 +0530 Subject: [PATCH 051/856] Added api to whitelist --- src/utils/whitelistApis.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index ebd557e1..29d57a05 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -537,6 +537,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/user/profileRegistry/createUserRegistry': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/portal/deptAction': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -901,6 +908,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/user/notifications/settings': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/reset': { checksNeeded: [], // tslint:disable-next-line: object-literal-sort-keys @@ -983,6 +997,7 @@ export const API_LIST = { '/protected/v8/user/profileDetails/createUser', '/protected/v8/user/profileRegistry/getUserRegistryByUser/:id', '/protected/v8/user/profileRegistry/createUserRegistryV2', + '/protected/v8/user/profileRegistry/createUserRegistry', '/protected/v8/portal/deptAction', '/protected/v8/workflowhandler/historyByApplicationId/:applicationId', '/protected/v8/user/autocomplete/:query', @@ -1035,6 +1050,7 @@ export const API_LIST = { '/protected/v8/user/profileRegistry/getMasterNationalities', '/protected/v8/user/profileRegistry/getMasterLanguages', '/protected/v8/user/profileRegistry/getProfilePageMeta', + '/protected/v8/user/notifications/settings', '/reset', ], } From b0767f9fc98365525a21072e2555a2b770df7617 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Thu, 29 Jul 2021 20:27:17 +0530 Subject: [PATCH 052/856] Added api to whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 29d57a05..f4f546d8 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -152,6 +152,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/action/content/v3/reject/:do_id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/discussion/forum/v2/read': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -957,6 +964,7 @@ export const API_LIST = { '/proxies/v8/action/content/v3/review/:do_id', '/proxies/v8/discussion/recent', '/proxies/v8/action/content/v3/publish/:do_id', + '/proxies/v8/action/content/v3/reject/:do_id', '/proxies/v8/user/private/v1/assign/role', '/proxies/v8/user/v1/search', '/proxies/v8/org/v1/read', From f2cdad0e8718eaaeee837209706ecae817b2e264 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Thu, 29 Jul 2021 20:27:17 +0530 Subject: [PATCH 053/856] Added api to whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 29d57a05..f4f546d8 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -152,6 +152,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/action/content/v3/reject/:do_id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/discussion/forum/v2/read': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -957,6 +964,7 @@ export const API_LIST = { '/proxies/v8/action/content/v3/review/:do_id', '/proxies/v8/discussion/recent', '/proxies/v8/action/content/v3/publish/:do_id', + '/proxies/v8/action/content/v3/reject/:do_id', '/proxies/v8/user/private/v1/assign/role', '/proxies/v8/user/v1/search', '/proxies/v8/org/v1/read', From 730dcb5e11d57d1cf4de840380c8f8697cd79e9c Mon Sep 17 00:00:00 2001 From: gohilamariappan <41056032+gohilamariappan@users.noreply.github.com> Date: Thu, 29 Jul 2021 21:10:44 +0530 Subject: [PATCH 054/856] notification configured --- Jenkinsfile-sun | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile-sun b/Jenkinsfile-sun index 3e653d5c..145ec00c 100644 --- a/Jenkinsfile-sun +++ b/Jenkinsfile-sun @@ -54,5 +54,7 @@ node('build-slave') { currentBuild.result = "FAILURE" throw err } - + finally { + email_notify() + } } From a3872c6eea6040831cae0709e1af1d488d1cc65c Mon Sep 17 00:00:00 2001 From: gohilamariappan <41056032+gohilamariappan@users.noreply.github.com> Date: Thu, 29 Jul 2021 21:15:24 +0530 Subject: [PATCH 055/856] mail test --- Jenkinsfile-sun | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Jenkinsfile-sun b/Jenkinsfile-sun index 145ec00c..d9a8a9b9 100644 --- a/Jenkinsfile-sun +++ b/Jenkinsfile-sun @@ -53,8 +53,8 @@ node('build-slave') { catch (err) { currentBuild.result = "FAILURE" throw err - } + } finally { email_notify() - } + } } From df84c96bf0a1d27650048ce8057110440f2d52e0 Mon Sep 17 00:00:00 2001 From: gohilamariappan <41056032+gohilamariappan@users.noreply.github.com> Date: Thu, 29 Jul 2021 21:20:25 +0530 Subject: [PATCH 056/856] notification test --- Jenkinsfile-sun | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Jenkinsfile-sun b/Jenkinsfile-sun index d9a8a9b9..a6e72f13 100644 --- a/Jenkinsfile-sun +++ b/Jenkinsfile-sun @@ -41,20 +41,21 @@ node('build-slave') { sh("bash -x build.sh ${build_tag} ${env.NODE_NAME} ${docker_server}") } + stage('ArchiveArtifacts') { + sh ("echo ${build_tag} > build_tag.txt") archiveArtifacts "metadata.json" - sh ("echo ${build_tag} > build_tag.txt") archiveArtifacts "build_tag.txt" currentBuild.description = "${build_tag}" } - + } } catch (err) { currentBuild.result = "FAILURE" throw err - } + } finally { email_notify() - } + } } From 5f9cba8bff555f71d0a5db5622f479e5cdff2d28 Mon Sep 17 00:00:00 2001 From: gohilamariappan <41056032+gohilamariappan@users.noreply.github.com> Date: Thu, 29 Jul 2021 21:44:21 +0530 Subject: [PATCH 057/856] reverted notification changes --- Jenkinsfile-sun | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Jenkinsfile-sun b/Jenkinsfile-sun index a6e72f13..173a8194 100644 --- a/Jenkinsfile-sun +++ b/Jenkinsfile-sun @@ -21,7 +21,7 @@ node('build-slave') { build_tag = sh(script: "echo " + params.github_release_tag.split('/')[-1] + "_" + commit_hash + "_" + env.BUILD_NUMBER, returnStdout: true).trim() echo "build_tag: " + build_tag - stage('docker-pre-build') { + stage('docker-pre-build') { sh ''' pwd docker build -f ./Dockerfile.build -t $docker_pre_build . @@ -55,7 +55,5 @@ node('build-slave') { currentBuild.result = "FAILURE" throw err } - finally { - email_notify() - } + } From b5a9a8ef1928bdf73eaeaf882806fdd6b629f8eb Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Fri, 30 Jul 2021 10:33:34 +0530 Subject: [PATCH 058/856] Added api to whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index f4f546d8..fcc0d5a4 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -922,6 +922,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/user/profileRegistry/searchUserRegistry': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/reset': { checksNeeded: [], // tslint:disable-next-line: object-literal-sort-keys @@ -1059,6 +1066,7 @@ export const API_LIST = { '/protected/v8/user/profileRegistry/getMasterLanguages', '/protected/v8/user/profileRegistry/getProfilePageMeta', '/protected/v8/user/notifications/settings', + '/protected/v8/user/profileRegistry/searchUserRegistry', '/reset', ], } From 77359ead12da44eb4499135957850e142a7bdea9 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Fri, 30 Jul 2021 10:33:34 +0530 Subject: [PATCH 059/856] Added api to whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index f4f546d8..fcc0d5a4 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -922,6 +922,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/user/profileRegistry/searchUserRegistry': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/reset': { checksNeeded: [], // tslint:disable-next-line: object-literal-sort-keys @@ -1059,6 +1066,7 @@ export const API_LIST = { '/protected/v8/user/profileRegistry/getMasterLanguages', '/protected/v8/user/profileRegistry/getProfilePageMeta', '/protected/v8/user/notifications/settings', + '/protected/v8/user/profileRegistry/searchUserRegistry', '/reset', ], } From 47e0437c3fbf72830d45c967bed9ed85f7c3890e Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Fri, 30 Jul 2021 10:52:23 +0530 Subject: [PATCH 060/856] Added api to whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index fcc0d5a4..8c15d4bd 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -38,6 +38,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/authApi/content/v3/update/:do_id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/user/details': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -941,6 +948,7 @@ export const API_LIST = { [ '/authApi/content/v3/create', '/authApi/content/v3/read/:do_id', + '/authApi/content/v3/update/:do_id', '/proxies/v8/api/user/v2/read', '/proxies/v8/api/user/v2/read/:id', '/proxies/v8/user/v1/read/:id', From 63528f3e742a46be2312de9f0c6608b2c2ab971b Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Fri, 30 Jul 2021 11:18:54 +0530 Subject: [PATCH 061/856] Added new api to whitelist --- src/utils/whitelistApis.ts | 48 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 8c15d4bd..9521e33c 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -936,6 +936,48 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/workflowhandler/transition': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/workflowhandler/nextActionSearch/:serviceName/:state': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/workflowhandler/historyByApplicationIdAndWfId/:applicationId/:wfId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/workflowhandler/workflowProcess/:wfId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/workflowhandler/updateUserProfileWf': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/workflowhandler/userWfSearch': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/reset': { checksNeeded: [], // tslint:disable-next-line: object-literal-sort-keys @@ -1075,6 +1117,12 @@ export const API_LIST = { '/protected/v8/user/profileRegistry/getProfilePageMeta', '/protected/v8/user/notifications/settings', '/protected/v8/user/profileRegistry/searchUserRegistry', + '/protected/v8/workflowhandler/transition', + '/protected/v8/workflowhandler/nextActionSearch/:serviceName/:state', + '/protected/v8/workflowhandler/historyByApplicationIdAndWfId/:applicationId/:wfId', + '/protected/v8/workflowhandler/workflowProcess/:wfId', + '/protected/v8/workflowhandler/updateUserProfileWf', + '/protected/v8/workflowhandler/userWfSearch', '/reset', ], } From a3d2a7afa3dcbc55a8d40aeb1bcba557b3c8d073 Mon Sep 17 00:00:00 2001 From: SuvajitTarento <72616697+SuvajitTarento@users.noreply.github.com> Date: Fri, 30 Jul 2021 11:27:06 +0530 Subject: [PATCH 062/856] Added new api to whitelist (#21) * Added api to whitelist * Added api to whitelist * Added api to whitelist * Added api to whitelist * Added new api to whitelist Co-authored-by: Amit Sengar --- src/utils/whitelistApis.ts | 56 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index fcc0d5a4..9521e33c 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -38,6 +38,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/authApi/content/v3/update/:do_id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/user/details': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -929,6 +936,48 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/workflowhandler/transition': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/workflowhandler/nextActionSearch/:serviceName/:state': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/workflowhandler/historyByApplicationIdAndWfId/:applicationId/:wfId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/workflowhandler/workflowProcess/:wfId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/workflowhandler/updateUserProfileWf': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/workflowhandler/userWfSearch': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/reset': { checksNeeded: [], // tslint:disable-next-line: object-literal-sort-keys @@ -941,6 +990,7 @@ export const API_LIST = { [ '/authApi/content/v3/create', '/authApi/content/v3/read/:do_id', + '/authApi/content/v3/update/:do_id', '/proxies/v8/api/user/v2/read', '/proxies/v8/api/user/v2/read/:id', '/proxies/v8/user/v1/read/:id', @@ -1067,6 +1117,12 @@ export const API_LIST = { '/protected/v8/user/profileRegistry/getProfilePageMeta', '/protected/v8/user/notifications/settings', '/protected/v8/user/profileRegistry/searchUserRegistry', + '/protected/v8/workflowhandler/transition', + '/protected/v8/workflowhandler/nextActionSearch/:serviceName/:state', + '/protected/v8/workflowhandler/historyByApplicationIdAndWfId/:applicationId/:wfId', + '/protected/v8/workflowhandler/workflowProcess/:wfId', + '/protected/v8/workflowhandler/updateUserProfileWf', + '/protected/v8/workflowhandler/userWfSearch', '/reset', ], } From 8b142f7056a872b1969e42c20802cc7167c3150f Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Fri, 30 Jul 2021 11:42:07 +0530 Subject: [PATCH 063/856] Added new api to whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 9521e33c..94c53f0a 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -411,6 +411,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/user/v1/migrate': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/connections/connections/recommended/userDepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1039,6 +1046,7 @@ export const API_LIST = { '/proxies/v8/discussion/user/:userKey/downvoted', '/proxies/v8/discussion/categories', '/proxies/v8/user/v1/autocomplete/:key', + '/proxies/v8/user/v1/migrate', '/protected/v8/user/profileDetails/test', '/protected/v8/resource/', '/protected/v8/user/details', From f053b577e0fb0e2680514fe2b245391513983de4 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Fri, 30 Jul 2021 13:12:33 +0530 Subject: [PATCH 064/856] Added apis in whitelist --- src/utils/whitelistApis.ts | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 94c53f0a..5bebcad7 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -369,6 +369,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/discussion/v2/topics/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/discussion/tags/': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -418,6 +425,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/discussion/v2/posts/:id/vote': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/connections/connections/recommended/userDepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -985,6 +999,20 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/user/profileRegistry/getUserRegistryById': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/user/profileRegistry/getUserRegistryById/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/reset': { checksNeeded: [], // tslint:disable-next-line: object-literal-sort-keys @@ -1008,6 +1036,7 @@ export const API_LIST = { '/proxies/v8/org/v1/search', '/proxies/v8/org/v1/update', '/proxies/v8/discussion/topic/:id/:slug', + '/proxies/v8/discussion/v2/posts/:id/vote', '/proxies/v8/learner/course/v1/user/enrollment/list/:id', '/proxies/v8/read/content-progres/:do_id', '/proxies/v8/content-progres/:do_id', @@ -1040,6 +1069,7 @@ export const API_LIST = { '/proxies/v8/learnervm/private/content/v3/publish/:do_id', '/proxies/v8/learnervm/private/content/v3/review/:do_id', '/proxies/v8/discussion/v2/topics', + '/proxies/v8/discussion/v2/topics/:id', '/proxies/v8/discussion/tags/', '/proxies/v8/discussion/user/:userKey/bookmarks', '/proxies/v8/discussion/user/:userKey/upvoted', @@ -1131,6 +1161,8 @@ export const API_LIST = { '/protected/v8/workflowhandler/workflowProcess/:wfId', '/protected/v8/workflowhandler/updateUserProfileWf', '/protected/v8/workflowhandler/userWfSearch', + '/protected/v8/user/profileRegistry/getUserRegistryById', + '/protected/v8/user/profileRegistry/getUserRegistryById/:id', '/reset', ], } From 544b2400fa2d783832f054ef1f178e7765b0975b Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Fri, 30 Jul 2021 13:50:30 +0530 Subject: [PATCH 065/856] Added new apis to whitelist --- src/utils/whitelistApis.ts | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 5bebcad7..bc33c9b1 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -376,7 +376,7 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/proxies/v8/discussion/tags/': { + '/proxies/v8/discussion/tags/:tag': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -390,6 +390,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/discussion/user/:userKey/bookmark': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/discussion/user/:userKey/upvoted': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -432,6 +439,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/discussion/v2/posts/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/connections/connections/recommended/userDepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1037,6 +1051,7 @@ export const API_LIST = { '/proxies/v8/org/v1/update', '/proxies/v8/discussion/topic/:id/:slug', '/proxies/v8/discussion/v2/posts/:id/vote', + '/proxies/v8/discussion/v2/posts/:id', '/proxies/v8/learner/course/v1/user/enrollment/list/:id', '/proxies/v8/read/content-progres/:do_id', '/proxies/v8/content-progres/:do_id', @@ -1070,8 +1085,9 @@ export const API_LIST = { '/proxies/v8/learnervm/private/content/v3/review/:do_id', '/proxies/v8/discussion/v2/topics', '/proxies/v8/discussion/v2/topics/:id', - '/proxies/v8/discussion/tags/', + '/proxies/v8/discussion/tags/:tag', '/proxies/v8/discussion/user/:userKey/bookmarks', + '/proxies/v8/discussion/user/:userKey/bookmark', '/proxies/v8/discussion/user/:userKey/upvoted', '/proxies/v8/discussion/user/:userKey/downvoted', '/proxies/v8/discussion/categories', From 889771d7ef96f481199fb4b88c5c8b38725cb12c Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Fri, 30 Jul 2021 14:40:52 +0530 Subject: [PATCH 066/856] Added new api to whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index bc33c9b1..d20b0a8a 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -432,6 +432,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/user/private/v1/assign/role/userrole': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/discussion/v2/posts/:id/vote': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1093,6 +1100,7 @@ export const API_LIST = { '/proxies/v8/discussion/categories', '/proxies/v8/user/v1/autocomplete/:key', '/proxies/v8/user/v1/migrate', + '/proxies/v8/user/private/v1/assign/role/userrole', '/protected/v8/user/profileDetails/test', '/protected/v8/resource/', '/protected/v8/user/details', From 2f885e3a313c55526ba89d7b087e0d69e8c6ee50 Mon Sep 17 00:00:00 2001 From: SuvajitTarento <72616697+SuvajitTarento@users.noreply.github.com> Date: Fri, 30 Jul 2021 14:45:07 +0530 Subject: [PATCH 067/856] Added new api to whitelist (#24) --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index bc33c9b1..d20b0a8a 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -432,6 +432,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/user/private/v1/assign/role/userrole': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/discussion/v2/posts/:id/vote': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1093,6 +1100,7 @@ export const API_LIST = { '/proxies/v8/discussion/categories', '/proxies/v8/user/v1/autocomplete/:key', '/proxies/v8/user/v1/migrate', + '/proxies/v8/user/private/v1/assign/role/userrole', '/protected/v8/user/profileDetails/test', '/protected/v8/resource/', '/protected/v8/user/details', From 28e452e186817d12b90f3190d7e0173b7163122b Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Fri, 30 Jul 2021 18:51:45 +0530 Subject: [PATCH 068/856] Added api to whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index d20b0a8a..7441a5fe 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1034,6 +1034,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/roleactivity/lead': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/reset': { checksNeeded: [], // tslint:disable-next-line: object-literal-sort-keys @@ -1187,6 +1194,7 @@ export const API_LIST = { '/protected/v8/workflowhandler/userWfSearch', '/protected/v8/user/profileRegistry/getUserRegistryById', '/protected/v8/user/profileRegistry/getUserRegistryById/:id', + '/protected/v8/roleactivity/lead', '/reset', ], } From e0b5ed8f7cd89f6d0376df1acf71d2d1863e68ba Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Sat, 31 Jul 2021 12:38:04 +0530 Subject: [PATCH 069/856] Added api to whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 7441a5fe..985aba7e 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1041,6 +1041,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/workallocation/copy/workOrder': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/reset': { checksNeeded: [], // tslint:disable-next-line: object-literal-sort-keys @@ -1195,6 +1202,7 @@ export const API_LIST = { '/protected/v8/user/profileRegistry/getUserRegistryById', '/protected/v8/user/profileRegistry/getUserRegistryById/:id', '/protected/v8/roleactivity/lead', + '/protected/v8/workallocation/copy/workOrder', '/reset', ], } From e7a73aa3d87929057af8abc873adf9bf3f041992 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Sat, 31 Jul 2021 12:38:04 +0530 Subject: [PATCH 070/856] Added api to whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 7441a5fe..985aba7e 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1041,6 +1041,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/workallocation/copy/workOrder': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/reset': { checksNeeded: [], // tslint:disable-next-line: object-literal-sort-keys @@ -1195,6 +1202,7 @@ export const API_LIST = { '/protected/v8/user/profileRegistry/getUserRegistryById', '/protected/v8/user/profileRegistry/getUserRegistryById/:id', '/protected/v8/roleactivity/lead', + '/protected/v8/workallocation/copy/workOrder', '/reset', ], } From 289830a1136bf45921c78e3594811b499378f722 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Tue, 3 Aug 2021 11:29:47 +0530 Subject: [PATCH 071/856] WAT userId issue --- src/protectedApi_v8/workallocation.ts | 16 ++++++++-------- src/utils/requestExtract.ts | 10 ++++++++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/protectedApi_v8/workallocation.ts b/src/protectedApi_v8/workallocation.ts index 63f89be2..2262e663 100644 --- a/src/protectedApi_v8/workallocation.ts +++ b/src/protectedApi_v8/workallocation.ts @@ -5,7 +5,7 @@ import { axiosRequestConfig } from '../configs/request.config' import { CONSTANTS } from '../utils/env' import { logError } from '../utils/logger' import { ERROR } from '../utils/message' -import { extractAuthorizationFromRequest, extractUserIdFromRequest, extractUserToken } from '../utils/requestExtract' +import { extractAuthorizationFromRequest, extractUserId, extractUserToken } from '../utils/requestExtract' const workallocationV1Path = 'v1/workallocation' const workallocationV2Path = 'v2/workallocation' @@ -35,7 +35,7 @@ const workOrderIdFailedMessage = 'NO_WORKORDER_ID' workAllocationApi.post('/add', async (req, res) => { try { - const userId = extractUserIdFromRequest(req) + const userId = extractUserId(req) if (!userId) { res.status(400).send(userIdFailedMessage) return @@ -64,7 +64,7 @@ workAllocationApi.post('/add', async (req, res) => { workAllocationApi.post('/update', async (req, res) => { try { - const userId = extractUserIdFromRequest(req) + const userId = extractUserId(req) if (!userId) { res.status(400).send(userIdFailedMessage) return @@ -139,7 +139,7 @@ workAllocationApi.get('/user/autocomplete/:searchTerm', async (req, res) => { workAllocationApi.post('/v2/add', async (req, res) => { try { - const userId = extractUserIdFromRequest(req) + const userId = extractUserId(req) if (!userId) { res.status(400).send(userIdFailedMessage) return @@ -171,7 +171,7 @@ workAllocationApi.post('/v2/add', async (req, res) => { workAllocationApi.post('/v2/update', async (req, res) => { try { - const userId = extractUserIdFromRequest(req) + const userId = extractUserId(req) if (!userId) { res.status(400).send(userIdFailedMessage) return @@ -201,7 +201,7 @@ workAllocationApi.post('/v2/update', async (req, res) => { }) workAllocationApi.post('/add/workorder', async (req, res) => { try { - const userId = extractUserIdFromRequest(req) + const userId = extractUserId(req) if (!userId) { res.status(400).send(userIdFailedMessage) return @@ -231,7 +231,7 @@ workAllocationApi.post('/add/workorder', async (req, res) => { }) workAllocationApi.post('/update/workorder', async (req, res) => { try { - const userId = extractUserIdFromRequest(req) + const userId = extractUserId(req) if (!userId) { res.status(400).send(userIdFailedMessage) return @@ -345,7 +345,7 @@ workAllocationApi.get('/getWorkAllocationById/:workAllocationId', async (req, re workAllocationApi.post('/copy/workOrder', async (req, res) => { try { - const userId = extractUserIdFromRequest(req) + const userId = extractUserId(req) if (!userId) { res.status(400).send(userIdFailedMessage) return diff --git a/src/utils/requestExtract.ts b/src/utils/requestExtract.ts index 01dd0041..3175a652 100644 --- a/src/utils/requestExtract.ts +++ b/src/utils/requestExtract.ts @@ -25,6 +25,16 @@ export const extractUserIdFromRequest = (req: IAuthorizedRequest): string => { } return (req.kauth && req.kauth.grant.access_token.content.sub) as string } + +export const extractUserId = (req: IAuthorizedRequest): string => { + const wid = req.header('wid') + if (wid) { + return wid + } + const userId = (req.kauth && req.kauth.grant.access_token.content.sub) as string + return userId.split(':')[2] +} + export const extractUserNameFromRequest = (req: IAuthorizedRequest) => (req.kauth && req.kauth.grant.access_token.content.name) as string From f3bb6b4329bdfa3df9fc477f28394a773e9eac68 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Tue, 3 Aug 2021 12:42:59 +0530 Subject: [PATCH 072/856] Added Scope Check functions --- src/protectedApi_v8/user/profile-details.ts | 22 +++++++++++++++- src/utils/apiWhiteList.ts | 29 +++++++++++++++++++++ src/utils/permissionHelper.ts | 1 + src/utils/whitelistApis.ts | 27 ++++++++++++------- 4 files changed, 68 insertions(+), 11 deletions(-) diff --git a/src/protectedApi_v8/user/profile-details.ts b/src/protectedApi_v8/user/profile-details.ts index c6bda720..de197a87 100644 --- a/src/protectedApi_v8/user/profile-details.ts +++ b/src/protectedApi_v8/user/profile-details.ts @@ -8,6 +8,9 @@ import { logError, logInfo } from '../../utils/logger' import { ERROR } from '../../utils/message' import { extractUserIdFromRequest, extractUserToken } from '../../utils/requestExtract' +const uuidv1 = require('uuid/v1') +const dateFormat = require('dateformat') + const API_END_POINTS = { createOSUserRegistry: (userId: string) => `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/create/profile?userId=${userId}`, createSb: `${CONSTANTS.KONG_API_BASE}/user/v3/create`, @@ -235,7 +238,24 @@ profileDeatailsApi.post('/createUser', async (req, res) => { url: API_END_POINTS.kongSearchUser, }) if (searchresponse.data.result.response.count > 0) { - res.status(400).send(emailAdressExist) + res.status(400).send( + { + id: 'api.error.createUser', + ver: '1.0', + // tslint:disable-next-line: object-literal-sort-keys + ts: dateFormat(new Date(), 'yyyy-mm-dd HH:MM:ss:lo'), + params: + { + resmsgid: uuidv1(), + // tslint:disable-next-line: object-literal-sort-keys + msgid: null, + status: 'failed', + err: 'USR_EMAIL_EXISTS', + errmsg: emailAdressExist, + }, + responseCode: 'USR_EMAIL_EXISTS', + result: {}, + }) return } else { const sbUserProfile: Partial = { diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index e44432ec..172cfeb1 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -61,6 +61,35 @@ const urlChecks = { return reject('User doesn\'t have appropriate roles') } }, + // tslint:disable-next-line: no-any + SCOPE_CHECK : (resolve: any, reject: any, req: Request, rolesForURL: any, REQ_URL: any) => { + logInfo('Portal_API_WHITELIST_SCOPE_CHECK : Middleware for URL [ ' + REQ_URL + ' ]') + const orgData = (_.get(req, 'session.orgs')) ? _.get(req, 'session.orgs') : [] + const orgId = (_.get(req, 'query.orgId')) ? _.get(req, 'query.orgId') : '' + if (_.isEmpty(orgId) || _.isEmpty(orgData)) { + return reject('User doesn\'t have appropriate organisation for scope check 1') + } + // tslint:disable-next-line: no-any + let scopeRoles: any = [] + // tslint:disable-next-line: no-any + orgData.forEach((element: any) => { + if (orgId === element.organisationId) { + scopeRoles = element.roles + } + }) + + if (_.isEmpty(scopeRoles)) { + return reject('User doesn\'t have appropriate organisation for scope check') + } + + if (_.includes(rolesForURL, 'ALL') && scopeRoles.length > 0) { + resolve() + } else if (_.intersection(rolesForURL, scopeRoles).length > 0) { + resolve() + } else { + return reject('User doesn\'t have appropriate roles') + } + }, } /** diff --git a/src/utils/permissionHelper.ts b/src/utils/permissionHelper.ts index 132f3c8f..ecdc8f4d 100644 --- a/src/utils/permissionHelper.ts +++ b/src/utils/permissionHelper.ts @@ -11,6 +11,7 @@ export const PERMISSION_HELPER = { reqObj.session.userId = userData.result.response.id ? userData.result.response.id : userData.result.response.userId reqObj.session.userName = userData.result.response.userName reqObj.session.userRoles = userData.result.response.roles + reqObj.session.orgs = userData.result.response.organisations if (!_.includes(reqObj.session.userRoles, 'PUBLIC')) { reqObj.session.userRoles.push('PUBLIC') } diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 985aba7e..fa46b105 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1,23 +1,27 @@ 'use strict' const CHECK = { ROLE: 'ROLE_CHECK', + SCOPE: 'SCOPE_CHECK', } // Valid Roles const ROLE = { - COMPENTENCY_REVIEWER: 'competencyReviewer', + CBC_ADMIN: 'CBC_ADMIN', + CBC_MEMBER: 'CBC_MEMBER', CONTENT_CREATOR: 'CONTENT_CREATOR', + CONTENT_PUBLISHER: 'CONTENT_PUBLISHER', + CONTENT_REVIEWER: 'CONTENT_REVIEWER', EDITOR: 'EDITOR', - FRAC_ACCESS_COMPENTENCY: 'fracAccessCompetency', - FRAC_ADMIN: 'fracAdmin', - FRAC_REVIEWER_ONE: 'fracReviewerOne', - FRAC_REVIEWER_TWO: 'fracReviewerTwo', - IFU_MEMBER: 'IFUMember', - MDO_ADMIN: 'MDO ADMIN', - MEMBER: 'Member', + FRAC_ACCESS_COMPENTENCY: 'FRAC_COMPETENCY_MEMBER', + FRAC_ADMIN: 'FRAC_ADMIN', + FRAC_COMPETENCY_REVIEWER: 'FRAC_COMPETENCY_REVIEWER', + FRAC_REVIEWER_ONE: 'FRAC_REVIEWER_L1', + FRAC_REVIEWER_TWO: 'FRAC_REVIEWER_L2', + IFU_MEMBER: 'IFU_MEMBER', + MDO_ADMIN: 'MDO_ADMIN', PUBLIC: 'PUBLIC', - PUBLISHER: 'PUBLISHER', - REVIEWER: 'REVIEWER', + SPV_ADMIN: 'SPV_ADMIN', + WAT_MEMBER: 'WAT_MEMBER', } // All api list validations @@ -662,6 +666,9 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.PUBLIC, ], + SCOPE_CHECK: [ + ROLE.MDO_ADMIN, + ], }, '/protected/v8/workallocation/user/autocomplete/:searchTerm': { checksNeeded: [CHECK.ROLE], From 9872d77343e3147657d146d366025dd051510d1c Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Tue, 3 Aug 2021 13:02:00 +0530 Subject: [PATCH 073/856] Changed the proxy read base url --- src/proxies_v8/proxies_v8.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 7cb52b54..aef68631 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -184,7 +184,7 @@ proxiesV8.use('/read/content-progres/*', ) proxiesV8.use('/api/user/v2/read', - proxyCreatorToAppentUserId(express.Router(), `${CONSTANTS.SUNBIRD_PROXY_API_BASE}/user/v2/read/`) + proxyCreatorToAppentUserId(express.Router(), `${CONSTANTS.KONG_API_BASE}/user/v2/read/`) ) proxiesV8.use([ From 733ec5f05fa755d28ef7011cb58f8280a9d1a625 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Tue, 3 Aug 2021 18:12:14 +0530 Subject: [PATCH 074/856] User Id logs --- src/utils/requestExtract.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/utils/requestExtract.ts b/src/utils/requestExtract.ts index 3175a652..7980a37c 100644 --- a/src/utils/requestExtract.ts +++ b/src/utils/requestExtract.ts @@ -29,10 +29,15 @@ export const extractUserIdFromRequest = (req: IAuthorizedRequest): string => { export const extractUserId = (req: IAuthorizedRequest): string => { const wid = req.header('wid') if (wid) { - return wid + // tslint:disable-next-line: no-console + console.log('------------------- user id -------', wid) + return wid } const userId = (req.kauth && req.kauth.grant.access_token.content.sub) as string - return userId.split(':')[2] + const returnUserId = userId.split(':')[2] + // tslint:disable-next-line: no-console + console.log('------------------- returnUserId -------', returnUserId) + return returnUserId } export const extractUserNameFromRequest = (req: IAuthorizedRequest) => From 47ca7f22c6f894e2762d576eaed0fbccb88d9949 Mon Sep 17 00:00:00 2001 From: Amit Kumar Yadav <36166686+amit-kumaryadav@users.noreply.github.com> Date: Tue, 3 Aug 2021 18:59:18 +0530 Subject: [PATCH 075/856] Updated work allocation user id issue --- src/utils/requestExtract.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/requestExtract.ts b/src/utils/requestExtract.ts index 3175a652..5fa2258f 100644 --- a/src/utils/requestExtract.ts +++ b/src/utils/requestExtract.ts @@ -32,7 +32,8 @@ export const extractUserId = (req: IAuthorizedRequest): string => { return wid } const userId = (req.kauth && req.kauth.grant.access_token.content.sub) as string - return userId.split(':')[2] + const returnUserId = userId.split(':')[2] + return returnUserId } export const extractUserNameFromRequest = (req: IAuthorizedRequest) => From 34572b51bc73334f21278663da16cc1c02514306 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Wed, 4 Aug 2021 16:32:44 +0530 Subject: [PATCH 076/856] Added user migrate API --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index fa46b105..75dacc8c 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -436,6 +436,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/user/private/v1/migrate': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/user/private/v1/assign/role/userrole': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1121,6 +1128,7 @@ export const API_LIST = { '/proxies/v8/discussion/categories', '/proxies/v8/user/v1/autocomplete/:key', '/proxies/v8/user/v1/migrate', + '/proxies/v8/user/private/v1/migrate', '/proxies/v8/user/private/v1/assign/role/userrole', '/protected/v8/user/profileDetails/test', '/protected/v8/resource/', From 36d44719a3d041b0dd6a5a3de9033702057acf4d Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Wed, 4 Aug 2021 16:48:00 +0530 Subject: [PATCH 077/856] Fixed NPM errors --- src/utils/requestExtract.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/utils/requestExtract.ts b/src/utils/requestExtract.ts index 5fa2258f..3175a652 100644 --- a/src/utils/requestExtract.ts +++ b/src/utils/requestExtract.ts @@ -32,8 +32,7 @@ export const extractUserId = (req: IAuthorizedRequest): string => { return wid } const userId = (req.kauth && req.kauth.grant.access_token.content.sub) as string - const returnUserId = userId.split(':')[2] - return returnUserId + return userId.split(':')[2] } export const extractUserNameFromRequest = (req: IAuthorizedRequest) => From c016945e3df604a8e05eb67cc4ceed2e67098797 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Wed, 4 Aug 2021 17:03:10 +0530 Subject: [PATCH 078/856] Added api to whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 75dacc8c..f9f3c6b6 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -464,6 +464,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/learnervm/private/content/v3/retire/': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/connections/connections/recommended/userDepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1130,6 +1137,7 @@ export const API_LIST = { '/proxies/v8/user/v1/migrate', '/proxies/v8/user/private/v1/migrate', '/proxies/v8/user/private/v1/assign/role/userrole', + '/proxies/v8/learnervm/private/content/v3/retire/', '/protected/v8/user/profileDetails/test', '/protected/v8/resource/', '/protected/v8/user/details', From 345bd75495ab5dd55667e41eed19e821f874b422 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Thu, 5 Aug 2021 18:17:52 +0530 Subject: [PATCH 079/856] Added api to whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index f9f3c6b6..5e36db9c 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1069,6 +1069,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/roleactivity/information': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/reset': { checksNeeded: [], // tslint:disable-next-line: object-literal-sort-keys @@ -1226,6 +1233,7 @@ export const API_LIST = { '/protected/v8/user/profileRegistry/getUserRegistryById/:id', '/protected/v8/roleactivity/lead', '/protected/v8/workallocation/copy/workOrder', + '/protected/v8/roleactivity/information', '/reset', ], } From 4bc647355c5bd66f68edffcee2fd66e6f1849b9d Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Fri, 6 Aug 2021 14:05:58 +0530 Subject: [PATCH 080/856] Modified api whitelist --- src/utils/whitelistApis.ts | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 5e36db9c..c3df7400 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -936,7 +936,7 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/protected/v8/roleactivity': { + '/protected/v8/roleactivity/:txt': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -1055,13 +1055,6 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/protected/v8/roleactivity/lead': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, '/protected/v8/workallocation/copy/workOrder': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1069,13 +1062,6 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/protected/v8/roleactivity/information': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, '/reset': { checksNeeded: [], // tslint:disable-next-line: object-literal-sort-keys @@ -1214,7 +1200,7 @@ export const API_LIST = { '/protected/v8/portal/mdo/deptAction/userrole', '/protected/v8/user/profileDetails/updateUser', '/protected/v8/frac/addDataNodeBulk', - '/protected/v8/roleactivity', + '/protected/v8/roleactivity/:txt', '/protected/v8/connections/update/connection', '/protected/v8/workflowhandler/userWFApplicationFieldsSearch', '/protected/v8/user/details/detailV1', @@ -1231,9 +1217,7 @@ export const API_LIST = { '/protected/v8/workflowhandler/userWfSearch', '/protected/v8/user/profileRegistry/getUserRegistryById', '/protected/v8/user/profileRegistry/getUserRegistryById/:id', - '/protected/v8/roleactivity/lead', '/protected/v8/workallocation/copy/workOrder', - '/protected/v8/roleactivity/information', '/reset', ], } From 978a79e65aa98570f69a0dee2c23fe57b32d187c Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Fri, 6 Aug 2021 18:36:04 +0530 Subject: [PATCH 081/856] Fix for progress api --- src/protectedApi_v8/user/progress.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/protectedApi_v8/user/progress.ts b/src/protectedApi_v8/user/progress.ts index 086a2519..51313e0a 100644 --- a/src/protectedApi_v8/user/progress.ts +++ b/src/protectedApi_v8/user/progress.ts @@ -4,7 +4,7 @@ import { axiosRequestConfig } from '../../configs/request.config' import { CONSTANTS } from '../../utils/env' import { logError, logErrorHeading, logInfo } from '../../utils/logger' import { ERROR } from '../../utils/message' -import { extractUserIdFromRequest } from '../../utils/requestExtract' +import { extractUserId, extractUserIdFromRequest } from '../../utils/requestExtract' const API_END_POINTS = { // tslint:disable-next-line: max-line-length @@ -49,13 +49,14 @@ progressApi.get('/', async (req, res) => { res.status(400).send(ERROR.ERROR_NO_ORG_DATA) return } + const userId = extractUserId(req) const response = await axios({ ...axiosRequestConfig, headers: { rootOrg, }, method: 'GET', - url: API_END_POINTS.hash(extractUserIdFromRequest(req)), + url: API_END_POINTS.hash(userId), }) // tslint:disable-next-line: no-console console.log('get progress api response : ', response) @@ -80,8 +81,9 @@ progressApi.post('/', async (req, res) => { res.status(400).send(ERROR.ERROR_NO_ORG_DATA) return } + const userId = extractUserId(req) const response = await axios.post( - API_END_POINTS.hash(extractUserIdFromRequest(req)), + API_END_POINTS.hash(userId), req.body, { ...axiosRequestConfig, From 5990dd9351192f25e5e008d07e6a5f03a8448c75 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Fri, 6 Aug 2021 18:46:19 +0530 Subject: [PATCH 082/856] Fix for progress api --- src/protectedApi_v8/user/progress.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/protectedApi_v8/user/progress.ts b/src/protectedApi_v8/user/progress.ts index 51313e0a..c482d152 100644 --- a/src/protectedApi_v8/user/progress.ts +++ b/src/protectedApi_v8/user/progress.ts @@ -49,14 +49,13 @@ progressApi.get('/', async (req, res) => { res.status(400).send(ERROR.ERROR_NO_ORG_DATA) return } - const userId = extractUserId(req) const response = await axios({ ...axiosRequestConfig, headers: { rootOrg, }, method: 'GET', - url: API_END_POINTS.hash(userId), + url: API_END_POINTS.hash(extractUserId(req)), }) // tslint:disable-next-line: no-console console.log('get progress api response : ', response) @@ -81,9 +80,8 @@ progressApi.post('/', async (req, res) => { res.status(400).send(ERROR.ERROR_NO_ORG_DATA) return } - const userId = extractUserId(req) const response = await axios.post( - API_END_POINTS.hash(userId), + API_END_POINTS.hash(extractUserId(req)), req.body, { ...axiosRequestConfig, From 7a2011b358f70b01f3bc6324ae833960121e7ef3 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Sat, 7 Aug 2021 10:24:36 +0530 Subject: [PATCH 083/856] Added assessment API in whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index c3df7400..36ff6397 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1069,6 +1069,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/user/evaluate/assessment/submit/v2': { + checksNeeded: [], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -1219,5 +1226,6 @@ export const API_LIST = { '/protected/v8/user/profileRegistry/getUserRegistryById/:id', '/protected/v8/workallocation/copy/workOrder', '/reset', + '/protected/v8/user/evaluate/assessment/submit/v2', ], } From 7a049b4e386657e61f6494794becba93424be3d7 Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Sun, 8 Aug 2021 17:22:30 +0530 Subject: [PATCH 084/856] added uid for requests --- src/utils/proxyCreator.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index a52b5181..421678c7 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -21,6 +21,7 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { // condition has been added to set the session in nodebb req header if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create')) { proxyReq.setHeader('nodebb_authorization_token', 'Bearer ' + req.session.nodebb_authorization_token) + proxyReq.query._uid = req.session._uid } if (req.body) { @@ -57,9 +58,15 @@ proxy.on('proxyRes', (proxyRes: any, req: any, _res: any, ) => { // } if (req.originalUrl.includes('/discussion/user/v1/create')) { - const nodebb_auth_token = proxyRes.headers.nodebb_auth_token + // tslint:disable-next-line: no-console + console.log('proxyRes==>', proxyRes) + // tslint:disable-next-line: no-console + console.log('_res==>', _res) + const uid = proxyRes.result.userId.uid + const nodebbToken = '722686c6-2a2e-4b22-addf-c427261fbdc6' if (req.session) { - req.session.nodebb_authorization_token = nodebb_auth_token + req.session.nodebb_authorization_token = nodebbToken + req.session._uid = uid } } }) From 296ee6625241741e8b9a2d8ffeb04b3ccec9a736 Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Sun, 8 Aug 2021 17:53:35 +0530 Subject: [PATCH 085/856] hardcode uid --- src/utils/proxyCreator.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 421678c7..4eee6c50 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -58,15 +58,12 @@ proxy.on('proxyRes', (proxyRes: any, req: any, _res: any, ) => { // } if (req.originalUrl.includes('/discussion/user/v1/create')) { - // tslint:disable-next-line: no-console - console.log('proxyRes==>', proxyRes) // tslint:disable-next-line: no-console console.log('_res==>', _res) - const uid = proxyRes.result.userId.uid const nodebbToken = '722686c6-2a2e-4b22-addf-c427261fbdc6' if (req.session) { req.session.nodebb_authorization_token = nodebbToken - req.session._uid = uid + req.session._uid = 1 } } }) From 43f9a53cf2757933e7d1d8d66518fde4e6acc975 Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Sun, 8 Aug 2021 19:21:50 +0530 Subject: [PATCH 086/856] removed uid from proxy --- src/utils/proxyCreator.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 4eee6c50..851a74bc 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -21,7 +21,6 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { // condition has been added to set the session in nodebb req header if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create')) { proxyReq.setHeader('nodebb_authorization_token', 'Bearer ' + req.session.nodebb_authorization_token) - proxyReq.query._uid = req.session._uid } if (req.body) { From 65025c46f56645d8aa286201b831a1ddb43f2acc Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Sun, 8 Aug 2021 19:39:44 +0530 Subject: [PATCH 087/856] Added response on data --- src/utils/proxyCreator.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 851a74bc..197c3095 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -56,15 +56,19 @@ proxy.on('proxyRes', (proxyRes: any, req: any, _res: any, ) => { // }) // } - if (req.originalUrl.includes('/discussion/user/v1/create')) { - // tslint:disable-next-line: no-console - console.log('_res==>', _res) - const nodebbToken = '722686c6-2a2e-4b22-addf-c427261fbdc6' - if (req.session) { - req.session.nodebb_authorization_token = nodebbToken + // tslint:disable-next-line: no-any + proxyRes.on('data', (data: any) => { + if (req.originalUrl.includes('/discussion/user/v1/create')) { + // tslint:disable-next-line: no-console + console.log('_res==>', data) + const nodebbToken = '722686c6-2a2e-4b22-addf-c427261fbdc6' + if (req.session) { + req.session.nodebb_authorization_token = nodebbToken + } req.session._uid = 1 } - } + }) + }) export function proxyCreatorRoute(route: Router, targetUrl: string, timeout = 10000): Router { From 60519437ae16e85443a6f4075b97f4011afe65e6 Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Sun, 8 Aug 2021 20:44:38 +0530 Subject: [PATCH 088/856] more changes --- src/utils/proxyCreator.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 197c3095..76eded0a 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -21,12 +21,16 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { // condition has been added to set the session in nodebb req header if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create')) { proxyReq.setHeader('nodebb_authorization_token', 'Bearer ' + req.session.nodebb_authorization_token) + } if (req.body) { + req.body.uid = req.session.uid const bodyData = JSON.stringify(req.body) proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData)) proxyReq.write(bodyData) + } else { + req.originalUrl = `${req.originalUrl}?_uid=${req.session.uid}` } }) @@ -59,13 +63,17 @@ proxy.on('proxyRes', (proxyRes: any, req: any, _res: any, ) => { // tslint:disable-next-line: no-any proxyRes.on('data', (data: any) => { if (req.originalUrl.includes('/discussion/user/v1/create')) { - // tslint:disable-next-line: no-console - console.log('_res==>', data) + + if ((proxyRes.statusCode === 200 || proxyRes.statusCode === 201)) { + data = JSON.parse(data.toString('utf-8')) + // tslint:disable-next-line: no-console + console.log('_res==>', data) + req.session.uid = data.result.userId.uid + } const nodebbToken = '722686c6-2a2e-4b22-addf-c427261fbdc6' if (req.session) { req.session.nodebb_authorization_token = nodebbToken } - req.session._uid = 1 } }) From 98053b69ff4315b223d734066bf604ef331e44ac Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Sun, 8 Aug 2021 21:14:17 +0530 Subject: [PATCH 089/856] conslole log added --- src/utils/proxyCreator.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 76eded0a..37d07177 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -25,12 +25,14 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { } if (req.body) { - req.body.uid = req.session.uid + req.body._uid = req.session.uid const bodyData = JSON.stringify(req.body) proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData)) proxyReq.write(bodyData) } else { req.originalUrl = `${req.originalUrl}?_uid=${req.session.uid}` + // tslint:disable-next-line: no-console + console.log('url==>', req.originalUrl) } }) From 50348cdf8cd24314afb96c55eeee76609628267f Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Sun, 8 Aug 2021 22:29:07 +0530 Subject: [PATCH 090/856] more changes for uid --- src/utils/proxyCreator.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 37d07177..1046a416 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -21,18 +21,18 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { // condition has been added to set the session in nodebb req header if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create')) { proxyReq.setHeader('nodebb_authorization_token', 'Bearer ' + req.session.nodebb_authorization_token) - + proxyReq.originalUrl = `${proxyReq.originalUrl}?_uid=${req.session.uid}` + // tslint:disable-next-line: no-console + console.log('url==>', proxyReq.originalUrl) + if (req.body) { + req.body._uid = req.session.uid + } } if (req.body) { - req.body._uid = req.session.uid const bodyData = JSON.stringify(req.body) proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData)) proxyReq.write(bodyData) - } else { - req.originalUrl = `${req.originalUrl}?_uid=${req.session.uid}` - // tslint:disable-next-line: no-console - console.log('url==>', req.originalUrl) } }) From 88c68967d3ddbe037a7be7a6e2e9de54fb2a2659 Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Sun, 8 Aug 2021 22:41:54 +0530 Subject: [PATCH 091/856] more url changes --- src/utils/proxyCreator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 1046a416..9b537aea 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -21,9 +21,9 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { // condition has been added to set the session in nodebb req header if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create')) { proxyReq.setHeader('nodebb_authorization_token', 'Bearer ' + req.session.nodebb_authorization_token) - proxyReq.originalUrl = `${proxyReq.originalUrl}?_uid=${req.session.uid}` + req.originalUrl = `${req.originalUrl}?_uid=${req.session.uid}` // tslint:disable-next-line: no-console - console.log('url==>', proxyReq.originalUrl) + console.log('url==>', req.originalUrl) if (req.body) { req.body._uid = req.session.uid } From 99ccedc8ee85000da115aed0b5ea56384ef7bc2f Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Sun, 8 Aug 2021 23:04:46 +0530 Subject: [PATCH 092/856] rewriting proxy path --- src/utils/proxyCreator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 9b537aea..1bb75e62 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -21,9 +21,9 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { // condition has been added to set the session in nodebb req header if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create')) { proxyReq.setHeader('nodebb_authorization_token', 'Bearer ' + req.session.nodebb_authorization_token) - req.originalUrl = `${req.originalUrl}?_uid=${req.session.uid}` + proxyReq.path += `?_uid=${req.session.uid}` // tslint:disable-next-line: no-console - console.log('url==>', req.originalUrl) + console.log('url==>', proxyReq.pat) if (req.body) { req.body._uid = req.session.uid } From 6939cf3b8193e1091effb60664bc6b8743d0910d Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Mon, 9 Aug 2021 11:17:27 +0530 Subject: [PATCH 093/856] more changes for fixing df create post --- src/utils/proxyCreator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 1bb75e62..40bf2189 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -20,10 +20,10 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { // condition has been added to set the session in nodebb req header if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create')) { - proxyReq.setHeader('nodebb_authorization_token', 'Bearer ' + req.session.nodebb_authorization_token) + proxyReq.setHeader('Authorization', req.session.nodebb_authorization_token) proxyReq.path += `?_uid=${req.session.uid}` // tslint:disable-next-line: no-console - console.log('url==>', proxyReq.pat) + console.log('url==>', proxyReq.path) if (req.body) { req.body._uid = req.session.uid } From d196f6b3881a1dbd71593029b41bb8df454de063 Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Mon, 9 Aug 2021 11:39:53 +0530 Subject: [PATCH 094/856] more fixes --- src/utils/proxyCreator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 40bf2189..440e80cf 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -20,7 +20,7 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { // condition has been added to set the session in nodebb req header if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create')) { - proxyReq.setHeader('Authorization', req.session.nodebb_authorization_token) + proxyReq.setHeader('nodebb_authorization_token', req.session.nodebb_authorization_token) proxyReq.path += `?_uid=${req.session.uid}` // tslint:disable-next-line: no-console console.log('url==>', proxyReq.path) From 4a1a6299ab5767116182b8be7e7b7f398b52895a Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Mon, 9 Aug 2021 11:55:52 +0530 Subject: [PATCH 095/856] more fixes --- src/utils/proxyCreator.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 440e80cf..09b9b0c3 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -21,18 +21,23 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { // condition has been added to set the session in nodebb req header if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create')) { proxyReq.setHeader('nodebb_authorization_token', req.session.nodebb_authorization_token) - proxyReq.path += `?_uid=${req.session.uid}` // tslint:disable-next-line: no-console console.log('url==>', proxyReq.path) if (req.body) { req.body._uid = req.session.uid + } else { + proxyReq.path += `?_uid=${req.session.uid}` } + // tslint:disable-next-line: no-console + console.log('req.body=====>', req.body) } if (req.body) { const bodyData = JSON.stringify(req.body) proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData)) proxyReq.write(bodyData) + // tslint:disable-next-line: no-console + console.log('body data=====>', bodyData) } }) From 920cf2e4f791d3ee39917cdf4c223981bfff3a42 Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Mon, 9 Aug 2021 13:27:41 +0530 Subject: [PATCH 096/856] reverting token changes --- src/utils/proxyCreator.ts | 69 +++++---------------------------------- 1 file changed, 9 insertions(+), 60 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 09b9b0c3..172aa947 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -20,70 +20,24 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { // condition has been added to set the session in nodebb req header if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create')) { - proxyReq.setHeader('nodebb_authorization_token', req.session.nodebb_authorization_token) - // tslint:disable-next-line: no-console - console.log('url==>', proxyReq.path) - if (req.body) { - req.body._uid = req.session.uid - } else { - proxyReq.path += `?_uid=${req.session.uid}` - } - // tslint:disable-next-line: no-console - console.log('req.body=====>', req.body) + proxyReq.setHeader('Authorization', 'Bearer ' + req.session.nodebb_authorization_token) } if (req.body) { const bodyData = JSON.stringify(req.body) proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData)) proxyReq.write(bodyData) - // tslint:disable-next-line: no-console - console.log('body data=====>', bodyData) } }) // tslint:disable-next-line: no-any proxy.on('proxyRes', (proxyRes: any, req: any, _res: any, ) => { - // res.removeHeader('access-control-allow-origin') - delete proxyRes.headers['access-control-allow-origin'] - // write user session with roles - // if (req.originalUrl.includes('/user/v2/read')) { - // // tslint:disable-next-line: no-any - // proxyRes.on('data', (data: any) => { - // if ((proxyRes.statusCode === 200 || proxyRes.statusCode === 201)) { - // data = JSON.parse(data.toString('utf-8')) - // const roles = data.result.response.roles - // req.session.userId = data.result.response.id ? data.result.response.id : data.result.response.userId - // req.session.userName = data.result.response.userName - // req.session.userRoles = roles - // // console.log(req); - // // tslint:disable-next-line: only-arrow-functions - // req.session.save(function(error: string) { - // if (error) { - // // tslint:disable-next-line: no-console - // console.log(error) - // } - // }) - // } - // }) - // } - - // tslint:disable-next-line: no-any - proxyRes.on('data', (data: any) => { - if (req.originalUrl.includes('/discussion/user/v1/create')) { - - if ((proxyRes.statusCode === 200 || proxyRes.statusCode === 201)) { - data = JSON.parse(data.toString('utf-8')) - // tslint:disable-next-line: no-console - console.log('_res==>', data) - req.session.uid = data.result.userId.uid - } - const nodebbToken = '722686c6-2a2e-4b22-addf-c427261fbdc6' - if (req.session) { - req.session.nodebb_authorization_token = nodebbToken - } + if (req.originalUrl.includes('/discussion/user/v1/create')) { + const nodebb_auth_token = proxyRes.headers.nodebb_auth_token + if (req.session) { + req.session.nodebb_authorization_token = nodebb_auth_token } - }) - + } }) export function proxyCreatorRoute(route: Router, targetUrl: string, timeout = 10000): Router { @@ -203,20 +157,15 @@ export function proxyCreatorSunbirdSearch(route: Router, targetUrl: string, _tim export function proxyCreatorToAppentUserId(route: Router, targetUrl: string, _timeout = 10000): Router { route.all('/*', (req, res) => { - const originalUrl = req.originalUrl - const lastIndex = originalUrl.lastIndexOf('/') - const subStr = originalUrl.substr(lastIndex).substr(1).split('-').length - let userId = extractUserIdFromRequest(req).split(':')[2] - if (subStr === 5 && (originalUrl.substr(lastIndex).substr(1))) { - userId = originalUrl.substr(lastIndex).substr(1) - } + const userId = extractUserIdFromRequest(req).split(':') + // tslint:disable-next-line: no-console console.log('REQ_URL_ORIGINAL proxyCreatorToAppentUserId', req.originalUrl) proxy.web(req, res, { changeOrigin: true, ignorePath: true, - target: targetUrl + userId, // [userId.length - 1], + target: targetUrl + userId[userId.length - 1], }) }) return route From e9516f77ee7e582dbb23665aba1f6e1d3a17c2d3 Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Mon, 9 Aug 2021 13:40:05 +0530 Subject: [PATCH 097/856] Revert "reverting token changes" This reverts commit 920cf2e4f791d3ee39917cdf4c223981bfff3a42. --- src/utils/proxyCreator.ts | 69 ++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 9 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 172aa947..09b9b0c3 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -20,24 +20,70 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { // condition has been added to set the session in nodebb req header if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create')) { - proxyReq.setHeader('Authorization', 'Bearer ' + req.session.nodebb_authorization_token) + proxyReq.setHeader('nodebb_authorization_token', req.session.nodebb_authorization_token) + // tslint:disable-next-line: no-console + console.log('url==>', proxyReq.path) + if (req.body) { + req.body._uid = req.session.uid + } else { + proxyReq.path += `?_uid=${req.session.uid}` + } + // tslint:disable-next-line: no-console + console.log('req.body=====>', req.body) } if (req.body) { const bodyData = JSON.stringify(req.body) proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData)) proxyReq.write(bodyData) + // tslint:disable-next-line: no-console + console.log('body data=====>', bodyData) } }) // tslint:disable-next-line: no-any proxy.on('proxyRes', (proxyRes: any, req: any, _res: any, ) => { - if (req.originalUrl.includes('/discussion/user/v1/create')) { - const nodebb_auth_token = proxyRes.headers.nodebb_auth_token - if (req.session) { - req.session.nodebb_authorization_token = nodebb_auth_token + // res.removeHeader('access-control-allow-origin') + delete proxyRes.headers['access-control-allow-origin'] + // write user session with roles + // if (req.originalUrl.includes('/user/v2/read')) { + // // tslint:disable-next-line: no-any + // proxyRes.on('data', (data: any) => { + // if ((proxyRes.statusCode === 200 || proxyRes.statusCode === 201)) { + // data = JSON.parse(data.toString('utf-8')) + // const roles = data.result.response.roles + // req.session.userId = data.result.response.id ? data.result.response.id : data.result.response.userId + // req.session.userName = data.result.response.userName + // req.session.userRoles = roles + // // console.log(req); + // // tslint:disable-next-line: only-arrow-functions + // req.session.save(function(error: string) { + // if (error) { + // // tslint:disable-next-line: no-console + // console.log(error) + // } + // }) + // } + // }) + // } + + // tslint:disable-next-line: no-any + proxyRes.on('data', (data: any) => { + if (req.originalUrl.includes('/discussion/user/v1/create')) { + + if ((proxyRes.statusCode === 200 || proxyRes.statusCode === 201)) { + data = JSON.parse(data.toString('utf-8')) + // tslint:disable-next-line: no-console + console.log('_res==>', data) + req.session.uid = data.result.userId.uid + } + const nodebbToken = '722686c6-2a2e-4b22-addf-c427261fbdc6' + if (req.session) { + req.session.nodebb_authorization_token = nodebbToken + } } - } + }) + }) export function proxyCreatorRoute(route: Router, targetUrl: string, timeout = 10000): Router { @@ -157,15 +203,20 @@ export function proxyCreatorSunbirdSearch(route: Router, targetUrl: string, _tim export function proxyCreatorToAppentUserId(route: Router, targetUrl: string, _timeout = 10000): Router { route.all('/*', (req, res) => { - const userId = extractUserIdFromRequest(req).split(':') - + const originalUrl = req.originalUrl + const lastIndex = originalUrl.lastIndexOf('/') + const subStr = originalUrl.substr(lastIndex).substr(1).split('-').length + let userId = extractUserIdFromRequest(req).split(':')[2] + if (subStr === 5 && (originalUrl.substr(lastIndex).substr(1))) { + userId = originalUrl.substr(lastIndex).substr(1) + } // tslint:disable-next-line: no-console console.log('REQ_URL_ORIGINAL proxyCreatorToAppentUserId', req.originalUrl) proxy.web(req, res, { changeOrigin: true, ignorePath: true, - target: targetUrl + userId[userId.length - 1], + target: targetUrl + userId, // [userId.length - 1], }) }) return route From ccf80af04a3779619a8b1284359a4c1469efaac1 Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Mon, 9 Aug 2021 13:46:40 +0530 Subject: [PATCH 098/856] reverted code --- src/utils/proxyCreator.ts | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 09b9b0c3..c3f45347 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -21,15 +21,6 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { // condition has been added to set the session in nodebb req header if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create')) { proxyReq.setHeader('nodebb_authorization_token', req.session.nodebb_authorization_token) - // tslint:disable-next-line: no-console - console.log('url==>', proxyReq.path) - if (req.body) { - req.body._uid = req.session.uid - } else { - proxyReq.path += `?_uid=${req.session.uid}` - } - // tslint:disable-next-line: no-console - console.log('req.body=====>', req.body) } if (req.body) { @@ -68,21 +59,12 @@ proxy.on('proxyRes', (proxyRes: any, req: any, _res: any, ) => { // } // tslint:disable-next-line: no-any - proxyRes.on('data', (data: any) => { - if (req.originalUrl.includes('/discussion/user/v1/create')) { - - if ((proxyRes.statusCode === 200 || proxyRes.statusCode === 201)) { - data = JSON.parse(data.toString('utf-8')) - // tslint:disable-next-line: no-console - console.log('_res==>', data) - req.session.uid = data.result.userId.uid - } - const nodebbToken = '722686c6-2a2e-4b22-addf-c427261fbdc6' - if (req.session) { - req.session.nodebb_authorization_token = nodebbToken - } + if (req.originalUrl.includes('/discussion/user/v1/create')) { + const nodebb_auth_token = proxyRes.headers.nodebb_auth_token + if (req.session) { + req.session.nodebb_authorization_token = nodebb_auth_token } - }) + } }) From 9782eccdfe8db40a374d77ba62976427610db948 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Mon, 9 Aug 2021 19:26:08 +0530 Subject: [PATCH 099/856] Task55227-Changes for connections api --- src/protectedApi_v8/connections_v2.ts | 459 +++++++++++++++++++ src/protectedApi_v8/protectedApiV8.ts | 7 +- src/protectedApi_v8/user/profile-registry.ts | 1 + src/utils/env.ts | 2 +- src/utils/message.ts | 1 + src/utils/whitelistApis.ts | 74 ++- 6 files changed, 540 insertions(+), 4 deletions(-) create mode 100644 src/protectedApi_v8/connections_v2.ts diff --git a/src/protectedApi_v8/connections_v2.ts b/src/protectedApi_v8/connections_v2.ts new file mode 100644 index 00000000..cd760b0b --- /dev/null +++ b/src/protectedApi_v8/connections_v2.ts @@ -0,0 +1,459 @@ +import axios from 'axios' +import { Router } from 'express' +import { axiosRequestConfig } from '../configs/request.config' +import { CONSTANTS } from '../utils/env' +import { logError, logInfo } from '../utils/logger' +import { ERROR } from '../utils/message' +import { extractUserId, extractUserIdFromRequest } from '../utils/requestExtract' +import { extractUserToken } from '../utils/requestExtract' + +const _ = require('lodash') + +const unknown = 'Connections Apis:- Failed due to unknown reason' +const apiEndpoints = { + detail: `${CONSTANTS.USER_PROFILE_API_BASE}/user/multi-fetch/wid`, + getConnectionEstablishedData: `${CONSTANTS.KONG_API_BASE}/connections/profile/fetch/established`, + getConnectionRequestsData: `${CONSTANTS.KONG_API_BASE}/connections/profile/fetch/requested`, + getConnectionRequestsReceivedData: `${CONSTANTS.KONG_API_BASE}/connections/profile/fetch/requests/received`, + getConnectionSuggestsData: `${CONSTANTS.KONG_API_BASE}/connections/profile/find/suggests`, + getUserOrgName: (userId: string) => `${CONSTANTS.KONG_API_BASE}/user/v1/search?userId=${userId}`, + getUserRegistryById: (userId: string) => `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/search/profile?userId=${userId}`, + postConnectionAddData: `${CONSTANTS.KONG_API_BASE}/connections/add`, + postConnectionRecommendationData: `${CONSTANTS.KONG_API_BASE}/connections/profile/find/recommended`, + postConnectionUpdateData: `${CONSTANTS.KONG_API_BASE}/connections/update`, +} + +export const connectionsV2Api = Router() + +connectionsV2Api.get('/v2/connections/requested', async (req, res) => { + try { + const rootOrg = req.headers.rootorg + const userId = extractUserIdFromRequest(req) + + if (!rootOrg) { + res.status(400).send(ERROR.ERROR_NO_ORG_DATA) + return + } + if (!userId) { + res.status(400).send(ERROR.GENERAL_ERR_MSG) + return + } + const response = await axios.get(apiEndpoints.getConnectionRequestsData, { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + rootOrg, + userId, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), + }, + }) + res.send((response.data)) + + } catch (err) { + logError('CONNECTIONS REQUESTS ERROR> ', err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: unknown, + } + ) + } +}) + +connectionsV2Api.get('/v2/connections/requests/received', async (req, res) => { + try { + const rootOrg = req.headers.rootorg + const userId = extractUserIdFromRequest(req) + + if (!rootOrg) { + res.status(400).send(ERROR.ERROR_NO_ORG_DATA) + return + } + if (!userId) { + res.status(400).send(ERROR.GENERAL_ERR_MSG) + return + } + const response = await axios.get(apiEndpoints.getConnectionRequestsReceivedData, { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + rootOrg, + userId, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), + }, + }) + res.send((response.data)) + + } catch (err) { + logError('CONNECTIONS REQUESTS ERROR> ', err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: unknown, + } + ) + } +}) + +connectionsV2Api.get('/v2/connections/established', async (req, res) => { + try { + const rootOrg = req.headers.rootorg + const userId = extractUserIdFromRequest(req) + + if (!rootOrg) { + res.status(400).send(ERROR.ERROR_NO_ORG_DATA) + return + } + if (!userId) { + res.status(400).send(ERROR.GENERAL_ERR_MSG) + return + } + const response = await axios.get(apiEndpoints.getConnectionEstablishedData, { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + rootOrg, + userId, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), + }, + }) + res.send((response.data)) + + } catch (err) { + logError('CONNECTIONS ERROR', err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: unknown, + } + ) + } +}) + +connectionsV2Api.get('/v2/connections/established/:id', async (req, res) => { + try { + const rootOrg = req.headers.rootorg + const userId = req.params.id + + if (!rootOrg) { + res.status(400).send(ERROR.ERROR_NO_ORG_DATA) + return + } + if (!userId) { + res.status(400).send(ERROR.GENERAL_ERR_MSG) + return + } + const response = await axios.get(apiEndpoints.getConnectionEstablishedData, { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + rootOrg, + userId, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), + }, + }) + res.send((response.data)) + + } catch (err) { + logError('CONNECTIONS ERROR', err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: unknown, + } + ) + } +}) + +connectionsV2Api.get('/v2/connections/suggests', async (req, res) => { + try { + const rootOrg = req.headers.rootorg + const userId = extractUserId(req) + + if (!rootOrg) { + res.status(400).send(ERROR.ERROR_NO_ORG_DATA) + return + } + if (!userId) { + res.status(400).send(ERROR.GENERAL_ERR_MSG) + return + } + const response = await axios.get(apiEndpoints.getConnectionSuggestsData, { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + rootOrg, + userId, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), + }, + }) + res.send((response.data)) + + } catch (err) { + logError('SUGGESTS ERROR >', err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: unknown, + } + ) + } +}) + +connectionsV2Api.post('/v2/add/connection', async (req, res) => { + try { + const rootOrg = req.header('rootorg') + const userIdFrom = extractUserIdFromRequest(req) + const userNameFrom = req.body.userNameFrom + const userDepartmentFrom = req.body.userDepartmentFrom + const userIdTo = req.body.userIdTo + const userNameTo = req.body.userNameTo + const userDepartmentTo = req.body.userDepartmentTo + + if (!rootOrg) { + res.status(400).send(ERROR.ERROR_NO_ORG_DATA) + return + } + if (!userIdFrom || !userIdTo || !userNameFrom || !userDepartmentFrom || !userNameTo || !userDepartmentTo) { + res.status(400).send(ERROR.GENERAL_ERR_MSG) + return + } + + const body = { + userDepartmentFrom, + userIdFrom, + userNameFrom, + + userDepartmentTo, + userIdTo, + userNameTo, + + } + const response = await axios.post( + apiEndpoints.postConnectionAddData, + body, + { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), + }, + } + ) + res.send(response.data) + + } catch (err) { + logError('ADD CONNECTION ERROR > ', err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: unknown, + } + ) + } +}) + +connectionsV2Api.post('/v2/update/connection', async (req, res) => { + try { + const rootOrg = req.header('rootorg') + const userNameFrom = req.body.userNameFrom + const userDepartmentFrom = req.body.userDepartmentFrom + const userIdFrom = extractUserIdFromRequest(req) + + const userNameTo = req.body.userNameTo + const userDepartmentTo = req.body.userDepartmentTo + const userIdTo = req.body.userIdTo + + const status = req.body.status + + if (!rootOrg) { + res.status(400).send(ERROR.ERROR_NO_ORG_DATA) + return + } + if (!userIdFrom || !userIdTo || !userNameFrom || !userDepartmentFrom || !userNameTo || !userDepartmentTo || !status) { + res.status(400).send(ERROR.GENERAL_ERR_MSG) + return + } + const body = { + status, + + userDepartmentFrom, + userIdFrom, + userNameFrom, + + userDepartmentTo, + userIdTo, + userNameTo, + } + const response = await axios.post( + apiEndpoints.postConnectionUpdateData, + body, + { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), + }, + } + ) + res.send(response.data) + + } catch (err) { + logError('UPDATE CONNECTION ERROR > ', err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: unknown, + } + ) + } +}) + +connectionsV2Api.post('/v2/connections/recommended', async (req, res) => { + try { + const body = req.body + const rootOrg = req.header('rootorg') + const userId = extractUserId(req) + + if (!rootOrg) { + res.status(400).send(ERROR.ERROR_NO_ORG_DATA) + return + } + if (!userId) { + res.status(400).send(ERROR.GENERAL_ERR_MSG) + return + } + + const response = await axios.post( + apiEndpoints.postConnectionRecommendationData, + body, + { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + rootOrg, + userId, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), + }, + } + ) + res.send(response.data) + + } catch (err) { + logError('RECOMMENDED ERROR > ', err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: unknown, + } + ) + } +}) + +connectionsV2Api.post('/v2/connections/recommended/userDepartment', async (req, res) => { + try { + let usrDept = '' + // tslint:disable-next-line: no-any + const userDepartment: any = [] + const rootOrg = req.header('rootorg') + const userId = extractUserId(req) + if (!rootOrg) { + res.status(400).send(ERROR.ERROR_NO_ORG_DATA) + return + } + if (!userId) { + res.status(400).send(ERROR.GENERAL_ERR_MSG) + return + } + + /** + * @author: Suvajit + * Step 1: Gets the User Department using search api + * Step 2: Loops through and gets all the dept + * Step 3: Send the data to recommandation api + */ + + const body = { + request : { + filters : { + userId, + }, + query: '', + }, + } + const url = `${apiEndpoints.getUserOrgName(userId)}` + const responseDetails = await axios.post( + url, + body, + { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + 'Content-Type': 'application/json', + 'X-Authenticated-User-Token': extractUserToken(req), + }, + } + ) + logInfo('responseDetails from /search/ : ', JSON.stringify(responseDetails.data)) + + // tslint:disable-next-line: no-any + const orgData: any = [] + const contentData = responseDetails.data.result.response.content + // tslint:disable-next-line: no-any + contentData.forEach((content: any) => { + const orgs = content.organisations + // tslint:disable-next-line: no-any + orgs.forEach((org: any) => { + orgData.push(org) + }) + }) + if (_.isEmpty(orgData)) { + res.status(400).send(ERROR.ERROR_NO_DEPT_DATA) + return + } + + // tslint:disable-next-line: no-any + orgData.forEach((element: any) => { + userDepartment.push(element.orgName) + }) + + usrDept = userDepartment || 'igot' + + const reqtoApi = { + offset: 0, + search: [ + { + field: 'employmentDetails.departmentName', + values: usrDept, + }, + ], + size: 5, + } + + const response = await axios.post( + apiEndpoints.postConnectionRecommendationData, + reqtoApi, + { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + rootOrg, + userId, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), + }, + } + ) + res.send(response.data) + + } catch (err) { + logError('RECOMMENDED ERROR > ', err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: unknown, + } + ) + } +}) diff --git a/src/protectedApi_v8/protectedApiV8.ts b/src/protectedApi_v8/protectedApiV8.ts index e234272d..9f92a395 100644 --- a/src/protectedApi_v8/protectedApiV8.ts +++ b/src/protectedApi_v8/protectedApiV8.ts @@ -7,7 +7,9 @@ import { certificationApi } from './certifications' import { cohortsApi } from './cohorts' import { competencyApi } from './competency' import { conceptGraphApi } from './concept' -import { connectionsApi } from './connections' +// tslint:disable-next-line: no-commented-code +// import { connectionsApi } from './connections' +import { connectionsV2Api } from './connections_v2' import { contentApi } from './content' import { contentValidationApi } from './contentValidation' import { counterApi } from './counter' @@ -66,7 +68,8 @@ protectedApiV8.use('/translate', translateApi) protectedApiV8.use('/attended-content', attendedContentApi) protectedApiV8.use('/event-external', externalEventsApi) protectedApiV8.use('/network', networkConnectionApi) -protectedApiV8.use('/connections', connectionsApi) +// protectedApiV8.use('/connections', connectionsApi) +protectedApiV8.use('/connections', connectionsV2Api) protectedApiV8.use('/competency', competencyApi) protectedApiV8.use('/dept', deptApi) protectedApiV8.use('/portal', portalApi) diff --git a/src/protectedApi_v8/user/profile-registry.ts b/src/protectedApi_v8/user/profile-registry.ts index d6b193be..d93730ff 100644 --- a/src/protectedApi_v8/user/profile-registry.ts +++ b/src/protectedApi_v8/user/profile-registry.ts @@ -97,6 +97,7 @@ profileRegistryApi.get('/getUserRegistry/:osid', async (req, res) => { } }) +// @Deprecated - Use Read API // tslint:disable-next-line: no-identical-functions profileRegistryApi.get('/getUserRegistryById', async (req, res) => { try { diff --git a/src/utils/env.ts b/src/utils/env.ts index 6f692ec7..c01de611 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -53,7 +53,7 @@ export const CONSTANTS = { KHUB_GRAPH_DATA: env.KHUB_GRAPH_DATA || 'http://localhost:3016', KHUB_SEARCH_BASE: env.KHUB_SEARCH_BASE || 'http://localhost:3014', KNOWLEDGE_MW_API_BASE: env.KNOWLEDGE_MW_API_BASE || 'http://knowledge-mw-service:5000', - KONG_API_BASE: env.KONG_API_BASE || 'http://kong:8000', + KONG_API_BASE: env.KONG_API_BASE || 'https://igot-dev.in/api', POST_ASSESSMENT_BASE: env.POST_ASSESSMENT_BASE || 'http://localhost.com', POST_ASSESSMENT_CLIENT_ID: env.POST_ASSESSMENT_CLIENT_ID || '', POST_ASSESSMENT_CLIENT_SECRET: env.POST_ASSESSMENT_CLIENT_SECRET || diff --git a/src/utils/message.ts b/src/utils/message.ts index 48d77780..3bb0f01d 100644 --- a/src/utils/message.ts +++ b/src/utils/message.ts @@ -1,4 +1,5 @@ export const ERROR = { + ERROR_NO_DEPT_DATA: 'Department details not available', ERROR_NO_ORG_DATA: 'ERROR_NO_ORG_DATA', GENERAL_ERR_MSG: 'Failed due to unknown reason', fetchErrorElasticSearch: 'ERROR_ELASTIC_SEARCH_CONNECTION', diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 36ff6397..193d1f27 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1070,7 +1070,70 @@ export const API_LIST = { ], }, '/protected/v8/user/evaluate/assessment/submit/v2': { - checksNeeded: [], + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/connections/v2/connections/recommended/userDepartment' : { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/connections/v2/connections/recommended': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/connections/v2/connections/requests/received': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/connections/v2/connections/established': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/connections/v2/connections/established/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/connections/v2/connections/requested': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/connections/v2/add/connection': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/connections/v2/connections/suggests': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/connections/v2/update/connection': { + checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, @@ -1227,5 +1290,14 @@ export const API_LIST = { '/protected/v8/workallocation/copy/workOrder', '/reset', '/protected/v8/user/evaluate/assessment/submit/v2', + '/protected/v8/connections/v2/connections/recommended/userDepartment', + '/protected/v8/connections/v2/connections/recommended', + '/protected/v8/connections/v2/connections/requests/received', + '/protected/v8/connections/v2/connections/established', + '/protected/v8/connections/v2/connections/established/:id', + '/protected/v8/connections/v2/connections/requested', + '/protected/v8/connections/v2/add/connection', + '/protected/v8/connections/v2/connections/suggests', + '/protected/v8/connections/v2/update/connection', ], } From 402ba4df6315b16a8bad92f7580df700f54b6500 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Mon, 9 Aug 2021 19:53:08 +0530 Subject: [PATCH 100/856] Changes for connection api --- src/protectedApi_v8/connections_v2.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/protectedApi_v8/connections_v2.ts b/src/protectedApi_v8/connections_v2.ts index cd760b0b..8b913588 100644 --- a/src/protectedApi_v8/connections_v2.ts +++ b/src/protectedApi_v8/connections_v2.ts @@ -16,7 +16,7 @@ const apiEndpoints = { getConnectionRequestsData: `${CONSTANTS.KONG_API_BASE}/connections/profile/fetch/requested`, getConnectionRequestsReceivedData: `${CONSTANTS.KONG_API_BASE}/connections/profile/fetch/requests/received`, getConnectionSuggestsData: `${CONSTANTS.KONG_API_BASE}/connections/profile/find/suggests`, - getUserOrgName: (userId: string) => `${CONSTANTS.KONG_API_BASE}/user/v1/search?userId=${userId}`, + getUserOrgName: `${CONSTANTS.KONG_API_BASE}/user/v1/search`, getUserRegistryById: (userId: string) => `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/search/profile?userId=${userId}`, postConnectionAddData: `${CONSTANTS.KONG_API_BASE}/connections/add`, postConnectionRecommendationData: `${CONSTANTS.KONG_API_BASE}/connections/profile/find/recommended`, @@ -383,7 +383,7 @@ connectionsV2Api.post('/v2/connections/recommended/userDepartment', async (req, query: '', }, } - const url = `${apiEndpoints.getUserOrgName(userId)}` + const url = `${apiEndpoints.getUserOrgName}` const responseDetails = await axios.post( url, body, From b18c5786fa321a67b7c2147bd54e0349f54e34e4 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Mon, 9 Aug 2021 20:30:16 +0530 Subject: [PATCH 101/856] Added api to whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 193d1f27..42cef818 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -471,6 +471,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/private/content/v3/update/:do_id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/connections/connections/recommended/userDepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1201,6 +1208,7 @@ export const API_LIST = { '/proxies/v8/user/private/v1/migrate', '/proxies/v8/user/private/v1/assign/role/userrole', '/proxies/v8/learnervm/private/content/v3/retire/', + '/proxies/v8/private/content/v3/update/:do_id', '/protected/v8/user/profileDetails/test', '/protected/v8/resource/', '/protected/v8/user/details', From 9378de9d3a2d245e1d96fb4d700606f3f0034186 Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Tue, 10 Aug 2021 12:47:28 +0530 Subject: [PATCH 102/856] change in auth token --- src/utils/proxyCreator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index c3f45347..4381ad86 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -20,7 +20,7 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { // condition has been added to set the session in nodebb req header if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create')) { - proxyReq.setHeader('nodebb_authorization_token', req.session.nodebb_authorization_token) + proxyReq.setHeader('nodebb_auth_token', req.session.nodebb_authorization_token) } if (req.body) { From 363488c85736a5b5aa0a3516dd6e5c3d6f8bb1bb Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Tue, 10 Aug 2021 12:54:03 +0530 Subject: [PATCH 103/856] nodebb auth token change --- src/utils/proxyCreator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 4381ad86..e1b3c85c 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -20,7 +20,7 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { // condition has been added to set the session in nodebb req header if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create')) { - proxyReq.setHeader('nodebb_auth_token', req.session.nodebb_authorization_token) + proxyReq.setHeader('nodebb_auth_token', req.session.nodebb_auth_token) } if (req.body) { @@ -62,7 +62,7 @@ proxy.on('proxyRes', (proxyRes: any, req: any, _res: any, ) => { if (req.originalUrl.includes('/discussion/user/v1/create')) { const nodebb_auth_token = proxyRes.headers.nodebb_auth_token if (req.session) { - req.session.nodebb_authorization_token = nodebb_auth_token + req.session.nodebb_auth_token = nodebb_auth_token } } From 097b8a0447796ceec4c2b187a44629ff9562ef5f Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Tue, 10 Aug 2021 19:20:02 +0530 Subject: [PATCH 104/856] hardcode master token --- src/utils/proxyCreator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index e1b3c85c..97e575e1 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -60,9 +60,9 @@ proxy.on('proxyRes', (proxyRes: any, req: any, _res: any, ) => { // tslint:disable-next-line: no-any if (req.originalUrl.includes('/discussion/user/v1/create')) { - const nodebb_auth_token = proxyRes.headers.nodebb_auth_token + const nodebbAuthtoken = '722686c6-2a2e-4b22-addf-c427261fbdc6' if (req.session) { - req.session.nodebb_auth_token = nodebb_auth_token + req.session.nodebb_auth_token = nodebbAuthtoken } } From 99aa739300e3c4aa5875cff875d4f3e282df8ed8 Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Wed, 11 Aug 2021 15:20:29 +0530 Subject: [PATCH 105/856] added uid in headers --- src/utils/proxyCreator.ts | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 97e575e1..bf9d1c6e 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -19,9 +19,6 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { proxyReq.setHeader('x-authenticated-userid', extractUserIdFromRequest(req)) // condition has been added to set the session in nodebb req header - if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create')) { - proxyReq.setHeader('nodebb_auth_token', req.session.nodebb_auth_token) - } if (req.body) { const bodyData = JSON.stringify(req.body) @@ -57,14 +54,22 @@ proxy.on('proxyRes', (proxyRes: any, req: any, _res: any, ) => { // } // }) // } - // tslint:disable-next-line: no-any - if (req.originalUrl.includes('/discussion/user/v1/create')) { - const nodebbAuthtoken = '722686c6-2a2e-4b22-addf-c427261fbdc6' - if (req.session) { - req.session.nodebb_auth_token = nodebbAuthtoken + proxyRes.on('data', (data: any) => { + if (req.originalUrl.includes('/discussion/user/v1/create')) { + + if ((proxyRes.statusCode === 200 || proxyRes.statusCode === 201)) { + data = JSON.parse(data.toString('utf-8')) + // tslint:disable-next-line: no-console + console.log('_res==>', data) + req.session.uid = data.result.userId.uid + } + const nodebbToken = '722686c6-2a2e-4b22-addf-c427261fbdc6' + if (req.session) { + req.session.nodebb_authorization_token = nodebbToken + } } - } + }) }) @@ -125,6 +130,13 @@ export function proxyCreatorSunbird(route: Router, targetUrl: string, _timeout = // tslint:disable-next-line: no-console console.log('REQ_URL_ORIGINAL proxyCreatorSunbird', req.originalUrl) + if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create') && req.session) { + if ( req.session && req.body) { + req.body._uid = req.session.uid + } else { + req.originalUrl += `?_uid=${req.session.uid}` + } + } const url = removePrefix(`${PROXY_SLUG}`, req.originalUrl) proxy.web(req, res, { changeOrigin: true, From 7470469090a90d3599701d0874b81c90227796f3 Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Wed, 11 Aug 2021 15:46:54 +0530 Subject: [PATCH 106/856] more changes --- src/utils/proxyCreator.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index bf9d1c6e..25eda402 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -131,7 +131,10 @@ export function proxyCreatorSunbird(route: Router, targetUrl: string, _timeout = // tslint:disable-next-line: no-console console.log('REQ_URL_ORIGINAL proxyCreatorSunbird', req.originalUrl) if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create') && req.session) { - if ( req.session && req.body) { + req.originalUrl += `?_uid=${req.session.uid}` + // tslint:disable-next-line: no-console + console.log('REQ_URL_ORIGINAL discussion', req.originalUrl) + if (req.session && req.body) { req.body._uid = req.session.uid } else { req.originalUrl += `?_uid=${req.session.uid}` From 2d4ce2c018db8502b61976e1d00c501cd2892a80 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Wed, 11 Aug 2021 15:52:34 +0530 Subject: [PATCH 107/856] Task55236-Changes for rootorgId --- src/utils/permissionHelper.ts | 1 + src/utils/proxyCreator.ts | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/utils/permissionHelper.ts b/src/utils/permissionHelper.ts index ecdc8f4d..76df6e8d 100644 --- a/src/utils/permissionHelper.ts +++ b/src/utils/permissionHelper.ts @@ -12,6 +12,7 @@ export const PERMISSION_HELPER = { reqObj.session.userName = userData.result.response.userName reqObj.session.userRoles = userData.result.response.roles reqObj.session.orgs = userData.result.response.organisations + reqObj.session.rootOrgId = userData.result.response.rootOrgId if (!_.includes(reqObj.session.userRoles, 'PUBLIC')) { reqObj.session.userRoles.push('PUBLIC') } diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 97e575e1..c4427f62 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -4,6 +4,8 @@ import { extractUserIdFromRequest, extractUserToken } from '../utils/requestExtr import { CONSTANTS } from './env' import { logInfo } from './logger' +const _ = require('lodash') + const proxyCreator = (timeout = 10000) => createProxyServer({ timeout, }) @@ -12,7 +14,7 @@ const PROXY_SLUG = '/proxies/v8' // tslint:disable-next-line: no-any proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { - proxyReq.setHeader('X-Channel-Id', CONSTANTS.X_Channel_Id) + proxyReq.setHeader('X-Channel-Id', (_.get(req, 'session.rootOrgId')) ? _.get(req, 'session.rootOrgId') : CONSTANTS.X_Channel_Id) // tslint:disable-next-line: max-line-length proxyReq.setHeader('Authorization', CONSTANTS.SB_API_KEY) proxyReq.setHeader('x-authenticated-user-token', extractUserToken(req)) From 287f11926b037f1cd312363fa9f6032e8f8f7f3d Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Wed, 11 Aug 2021 16:09:01 +0530 Subject: [PATCH 108/856] removed extra query params --- src/utils/proxyCreator.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index f1f92af4..7dba3300 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -133,7 +133,6 @@ export function proxyCreatorSunbird(route: Router, targetUrl: string, _timeout = // tslint:disable-next-line: no-console console.log('REQ_URL_ORIGINAL proxyCreatorSunbird', req.originalUrl) if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create') && req.session) { - req.originalUrl += `?_uid=${req.session.uid}` // tslint:disable-next-line: no-console console.log('REQ_URL_ORIGINAL discussion', req.originalUrl) if (req.session && req.body) { From 4e13919feba370bec4c374f689ad82af902d682d Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Wed, 11 Aug 2021 16:21:22 +0530 Subject: [PATCH 109/856] more uid changes --- src/utils/proxyCreator.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 7dba3300..bbace0dd 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -21,7 +21,17 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { proxyReq.setHeader('x-authenticated-userid', extractUserIdFromRequest(req)) // condition has been added to set the session in nodebb req header + if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/') && req.session) { + if (req.session && req.body) { + req.body._uid = req.session.uid + } else { + req.originalUrl += `?_uid=${req.session.uid}` + } + // tslint:disable-next-line: no-console + console.log('REQ_URL_ORIGINAL discussion', req.originalUrl) + + } if (req.body) { const bodyData = JSON.stringify(req.body) proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData)) @@ -132,15 +142,6 @@ export function proxyCreatorSunbird(route: Router, targetUrl: string, _timeout = // tslint:disable-next-line: no-console console.log('REQ_URL_ORIGINAL proxyCreatorSunbird', req.originalUrl) - if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create') && req.session) { - // tslint:disable-next-line: no-console - console.log('REQ_URL_ORIGINAL discussion', req.originalUrl) - if (req.session && req.body) { - req.body._uid = req.session.uid - } else { - req.originalUrl += `?_uid=${req.session.uid}` - } - } const url = removePrefix(`${PROXY_SLUG}`, req.originalUrl) proxy.web(req, res, { changeOrigin: true, From 121edde1d081058d1875b645d5fdb5b6e14ab0c7 Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Wed, 11 Aug 2021 16:30:13 +0530 Subject: [PATCH 110/856] removing uid query --- src/utils/proxyCreator.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index bbace0dd..2d87d086 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -25,12 +25,8 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { if (req.session && req.body) { req.body._uid = req.session.uid - } else { - req.originalUrl += `?_uid=${req.session.uid}` } - // tslint:disable-next-line: no-console - console.log('REQ_URL_ORIGINAL discussion', req.originalUrl) - + } if (req.body) { const bodyData = JSON.stringify(req.body) From ef6110faad3e82d085fd60f52add556b3e231791 Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Wed, 11 Aug 2021 16:40:35 +0530 Subject: [PATCH 111/856] removing some code --- src/utils/proxyCreator.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 2d87d086..f46f3757 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -21,12 +21,10 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { proxyReq.setHeader('x-authenticated-userid', extractUserIdFromRequest(req)) // condition has been added to set the session in nodebb req header - if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/') && req.session) { + if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/') && req.body) { + + req.body._uid = req.session.uid - if (req.session && req.body) { - req.body._uid = req.session.uid - } - } if (req.body) { const bodyData = JSON.stringify(req.body) From 499dba63078354922f72dde84759f1935f6a18b4 Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Thu, 12 Aug 2021 00:31:34 +0530 Subject: [PATCH 112/856] removing uid --- src/utils/proxyCreator.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index f46f3757..4438e65c 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -21,10 +21,9 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { proxyReq.setHeader('x-authenticated-userid', extractUserIdFromRequest(req)) // condition has been added to set the session in nodebb req header - if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/') && req.body) { - - req.body._uid = req.session.uid - + if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/')) { + // tslint:disable-next-line: no-console + console.log('test log ========================> test log') } if (req.body) { const bodyData = JSON.stringify(req.body) @@ -68,7 +67,8 @@ proxy.on('proxyRes', (proxyRes: any, req: any, _res: any, ) => { data = JSON.parse(data.toString('utf-8')) // tslint:disable-next-line: no-console console.log('_res==>', data) - req.session.uid = data.result.userId.uid + /* tslint:disable-next-line */ + // req.session.uid = data.result.userId.uid } const nodebbToken = '722686c6-2a2e-4b22-addf-c427261fbdc6' if (req.session) { From 176af6e257628d5673ca39f1374b2e67f971aae5 Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Thu, 12 Aug 2021 01:19:58 +0530 Subject: [PATCH 113/856] reverted back the uid code --- src/utils/proxyCreator.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 4438e65c..3443923b 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -21,9 +21,16 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { proxyReq.setHeader('x-authenticated-userid', extractUserIdFromRequest(req)) // condition has been added to set the session in nodebb req header - if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/')) { + if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create') && req.session) { + + if (req.body) { + req.body._uid = req.session.uid + } else { + req.originalUrl += `?_uid=${req.session.uid}` + } // tslint:disable-next-line: no-console - console.log('test log ========================> test log') + console.log('REQ_URL_ORIGINAL discussion', req.originalUrl) + } if (req.body) { const bodyData = JSON.stringify(req.body) @@ -67,8 +74,7 @@ proxy.on('proxyRes', (proxyRes: any, req: any, _res: any, ) => { data = JSON.parse(data.toString('utf-8')) // tslint:disable-next-line: no-console console.log('_res==>', data) - /* tslint:disable-next-line */ - // req.session.uid = data.result.userId.uid + req.session.uid = data.result.userId.uid } const nodebbToken = '722686c6-2a2e-4b22-addf-c427261fbdc6' if (req.session) { From f1a3b7145171df63592d9731183c3184de353f6a Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Thu, 12 Aug 2021 09:52:49 +0530 Subject: [PATCH 114/856] addid uid to proxyreq --- src/utils/proxyCreator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 3443923b..322080b1 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -26,10 +26,10 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { if (req.body) { req.body._uid = req.session.uid } else { - req.originalUrl += `?_uid=${req.session.uid}` + proxyReq.path += `?_uid=${req.session.uid}` } // tslint:disable-next-line: no-console - console.log('REQ_URL_ORIGINAL discussion', req.originalUrl) + console.log('REQ_URL_ORIGINAL discussion', proxyReq.path) } if (req.body) { From 669a25e551183eabdd57d22fbb4ca567747a2d55 Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Thu, 12 Aug 2021 10:28:52 +0530 Subject: [PATCH 115/856] added more fixes --- src/utils/proxyCreator.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 322080b1..d8abd0ce 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -21,12 +21,11 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { proxyReq.setHeader('x-authenticated-userid', extractUserIdFromRequest(req)) // condition has been added to set the session in nodebb req header + /* tslint:disable-next-line */ if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create') && req.session) { if (req.body) { req.body._uid = req.session.uid - } else { - proxyReq.path += `?_uid=${req.session.uid}` } // tslint:disable-next-line: no-console console.log('REQ_URL_ORIGINAL discussion', proxyReq.path) @@ -139,7 +138,14 @@ export function proxyCreatorLearner(route: Router, targetUrl: string, _timeout = export function proxyCreatorSunbird(route: Router, targetUrl: string, _timeout = 10000): Router { route.all('/*', (req, res) => { + if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create') && req.session) { + if (req.body) { + req.body._uid = req.session.uid + } else { + req.originalUrl += `?_uid=${req.session.uid}` + } + } // tslint:disable-next-line: no-console console.log('REQ_URL_ORIGINAL proxyCreatorSunbird', req.originalUrl) const url = removePrefix(`${PROXY_SLUG}`, req.originalUrl) From b629be24435e73f907eac8948fd1208218c219ee Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Thu, 12 Aug 2021 10:47:39 +0530 Subject: [PATCH 116/856] added more logic for uid --- src/utils/proxyCreator.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index d8abd0ce..0023e196 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -21,7 +21,7 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { proxyReq.setHeader('x-authenticated-userid', extractUserIdFromRequest(req)) // condition has been added to set the session in nodebb req header - /* tslint:disable-next-line */ + /* tslint:disable-next-line */ if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create') && req.session) { if (req.body) { @@ -138,17 +138,20 @@ export function proxyCreatorLearner(route: Router, targetUrl: string, _timeout = export function proxyCreatorSunbird(route: Router, targetUrl: string, _timeout = 10000): Router { route.all('/*', (req, res) => { + // tslint:disable-next-line: no-console + console.log('REQ_URL_ORIGINAL proxyCreatorSunbird', req.originalUrl) + let url = removePrefix(`${PROXY_SLUG}`, req.originalUrl) if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create') && req.session) { if (req.body) { req.body._uid = req.session.uid } else { - req.originalUrl += `?_uid=${req.session.uid}` + url += `?_uid=${req.session.uid}` } + // tslint:disable-next-line: no-console + console.log('REQ_URL_ORIGINAL proxyCreatorSunbird ======= discussion', req.originalUrl) } - // tslint:disable-next-line: no-console - console.log('REQ_URL_ORIGINAL proxyCreatorSunbird', req.originalUrl) - const url = removePrefix(`${PROXY_SLUG}`, req.originalUrl) + proxy.web(req, res, { changeOrigin: true, ignorePath: true, From c8f11aed9df42c2473ade143944ed6d6f4cbd6ab Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Thu, 12 Aug 2021 11:29:51 +0530 Subject: [PATCH 117/856] fixing the uid issue --- src/utils/proxyCreator.ts | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 0023e196..909e6b78 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -142,14 +142,9 @@ export function proxyCreatorSunbird(route: Router, targetUrl: string, _timeout = console.log('REQ_URL_ORIGINAL proxyCreatorSunbird', req.originalUrl) let url = removePrefix(`${PROXY_SLUG}`, req.originalUrl) if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create') && req.session) { - - if (req.body) { - req.body._uid = req.session.uid - } else { - url += `?_uid=${req.session.uid}` - } + url += `?_uid=${req.session.uid}` // tslint:disable-next-line: no-console - console.log('REQ_URL_ORIGINAL proxyCreatorSunbird ======= discussion', req.originalUrl) + console.log('REQ_URL_ORIGINAL proxyCreatorSunbird ======= discussion', url) } proxy.web(req, res, { From ac70906d3a17ac4ed7ea3378de93db213da533e9 Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Thu, 12 Aug 2021 11:32:34 +0530 Subject: [PATCH 118/856] more changes --- src/utils/proxyCreator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 909e6b78..11d80c15 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -142,7 +142,7 @@ export function proxyCreatorSunbird(route: Router, targetUrl: string, _timeout = console.log('REQ_URL_ORIGINAL proxyCreatorSunbird', req.originalUrl) let url = removePrefix(`${PROXY_SLUG}`, req.originalUrl) if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create') && req.session) { - url += `?_uid=${req.session.uid}` + url = `${url}?_uid=${req.session.uid}` // tslint:disable-next-line: no-console console.log('REQ_URL_ORIGINAL proxyCreatorSunbird ======= discussion', url) } From 2ab1bb53e69633c42077f588bcdea20fc0a4408f Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Thu, 12 Aug 2021 11:43:43 +0530 Subject: [PATCH 119/856] handling some edge cases for query params --- src/utils/proxyCreator.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 11d80c15..2c900a83 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -142,7 +142,11 @@ export function proxyCreatorSunbird(route: Router, targetUrl: string, _timeout = console.log('REQ_URL_ORIGINAL proxyCreatorSunbird', req.originalUrl) let url = removePrefix(`${PROXY_SLUG}`, req.originalUrl) if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create') && req.session) { - url = `${url}?_uid=${req.session.uid}` + if (req.originalUrl.includes('?')) { + url = `${url}&_uid=${req.session.uid}` + } else { + url = `${url}?_uid=${req.session.uid}` + } // tslint:disable-next-line: no-console console.log('REQ_URL_ORIGINAL proxyCreatorSunbird ======= discussion', url) } From df0dd7d003e3b4a2c82d9dd7b2728af2533af452 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Thu, 12 Aug 2021 20:52:13 +0530 Subject: [PATCH 120/856] Discussion hub api --- .../discussionHub/categories.ts | 20 +++- .../discussionHub/notifications.ts | 11 +- src/protectedApi_v8/discussionHub/posts.ts | 11 +- src/protectedApi_v8/discussionHub/tags.ts | 20 +++- src/protectedApi_v8/discussionHub/topics.ts | 60 ++++++++-- src/protectedApi_v8/discussionHub/users.ts | 111 ++++++++++++++---- src/protectedApi_v8/discussionHub/writeApi.ts | 83 ++++++++++--- 7 files changed, 243 insertions(+), 73 deletions(-) diff --git a/src/protectedApi_v8/discussionHub/categories.ts b/src/protectedApi_v8/discussionHub/categories.ts index 31e12376..bdeb36d9 100644 --- a/src/protectedApi_v8/discussionHub/categories.ts +++ b/src/protectedApi_v8/discussionHub/categories.ts @@ -4,13 +4,13 @@ import { getRootOrg } from '../../authoring/utils/header' import { axiosRequestConfig } from '../../configs/request.config' import { CONSTANTS } from '../../utils/env' import { logError, logInfo } from '../../utils/logger' -import { extractUserIdFromRequest } from '../../utils/requestExtract' +import { extractUserIdFromRequest, extractUserToken } from '../../utils/requestExtract' const API_ENDPOINTS = { - getAllCategories: `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/categories`, + getAllCategories: `${CONSTANTS.KONG_API_BASE}/nodebb/api/categories`, // tslint:disable-next-line: no-any getCategoryDetails: (cid: any, slug?: any, tid?: any) => { - let url = `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/category/${cid}` + let url = `${CONSTANTS.KONG_API_BASE}/nodebb/api/category/${cid}` if (slug) { url = `${url}/${slug}` } @@ -31,7 +31,12 @@ categoriesApi.get('/', async (req, res) => { const url = API_ENDPOINTS.getAllCategories const response = await axios.get( url, - { ...axiosRequestConfig, headers: { rootOrg } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) res.send(response.data) } catch (err) { @@ -54,7 +59,12 @@ categoriesApi.get('/:cid/:slug?/:tid?', async (req, res) => { const url = API_ENDPOINTS.getCategoryDetails(cid, slug, tid) const response = await axios.get( `${url}?page=${pageNo}&sort=${sort}`, - { ...axiosRequestConfig, headers: { rootOrg } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) res.send(response.data) } catch (err) { diff --git a/src/protectedApi_v8/discussionHub/notifications.ts b/src/protectedApi_v8/discussionHub/notifications.ts index 9be53dd1..5b6b278b 100644 --- a/src/protectedApi_v8/discussionHub/notifications.ts +++ b/src/protectedApi_v8/discussionHub/notifications.ts @@ -5,10 +5,10 @@ import { axiosRequestConfig } from '../../configs/request.config' import { getUserUID, getWriteApiToken } from '../../utils/discussionHub-helper' import { CONSTANTS } from '../../utils/env' import { logError, logInfo } from '../../utils/logger' -import { extractUserIdFromRequest } from '../../utils/requestExtract' +import { extractUserIdFromRequest , extractUserToken} from '../../utils/requestExtract' const API_ENDPOINTS = { - getNotifications: `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/notifications`, + getNotifications: `${CONSTANTS.KONG_API_BASE}/nodebb/auth/api/notifications`, } export const notificationsApi = Router() @@ -22,7 +22,12 @@ notificationsApi.get('/', async (req, res) => { const url = API_ENDPOINTS.getNotifications + `?_uid=${userUid}` const response = await axios.get( url, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) res.send(response.data) } catch (err) { diff --git a/src/protectedApi_v8/discussionHub/posts.ts b/src/protectedApi_v8/discussionHub/posts.ts index dbcf6d0b..5f6abb50 100644 --- a/src/protectedApi_v8/discussionHub/posts.ts +++ b/src/protectedApi_v8/discussionHub/posts.ts @@ -4,10 +4,10 @@ import { getRootOrg } from '../../authoring/utils/header' import { axiosRequestConfig } from '../../configs/request.config' import { CONSTANTS } from '../../utils/env' import { logError, logInfo } from '../../utils/logger' -import { extractUserIdFromRequest } from '../../utils/requestExtract' +import { extractUserIdFromRequest, extractUserToken} from '../../utils/requestExtract' const API_ENDPOINTS = { - getPosts: (term: string) => `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/recent/posts/${term}`, + getPosts: (term: string) => `${CONSTANTS.DISCUSSION_HUB_API_BASE}/nodebb/api/recent/posts/${term}`, } export const postsApi = Router() @@ -21,7 +21,12 @@ postsApi.get('/:term', async (req, res) => { const url = API_ENDPOINTS.getPosts(term) const response = await axios.get( url, - { ...axiosRequestConfig, headers: { rootOrg } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) res.send(response.data) } catch (err) { diff --git a/src/protectedApi_v8/discussionHub/tags.ts b/src/protectedApi_v8/discussionHub/tags.ts index 7d547043..f119e9f4 100644 --- a/src/protectedApi_v8/discussionHub/tags.ts +++ b/src/protectedApi_v8/discussionHub/tags.ts @@ -4,11 +4,11 @@ import { getRootOrg } from '../../authoring/utils/header' import { axiosRequestConfig } from '../../configs/request.config' import { CONSTANTS } from '../../utils/env' import { logError, logInfo } from '../../utils/logger' -import { extractUserIdFromRequest } from '../../utils/requestExtract' +import { extractUserIdFromRequest, extractUserToken} from '../../utils/requestExtract' const API_ENDPOINTS = { - getTagTopics: (tagName: string) => `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/tags/${tagName}`, - getTags: `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/tags`, + getTagTopics: (tagName: string) => `${CONSTANTS.KONG_API_BASE}/nodebb/api/tags/${tagName}`, + getTags: `${CONSTANTS.KONG_API_BASE}/nodebb/api/tags`, } export const tagsApi = Router() @@ -21,7 +21,12 @@ tagsApi.get('/', async (req, res) => { const url = API_ENDPOINTS.getTags const response = await axios.get( url, - { ...axiosRequestConfig, headers: { rootOrg } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) res.send(response.data) } catch (err) { @@ -39,7 +44,12 @@ tagsApi.get('/:tagName', async (req, res) => { const url = API_ENDPOINTS.getTagTopics(tagName) const response = await axios.get( url, - { ...axiosRequestConfig, headers: { rootOrg } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) res.send(response.data) } catch (err) { diff --git a/src/protectedApi_v8/discussionHub/topics.ts b/src/protectedApi_v8/discussionHub/topics.ts index 1be67f3e..f63051ad 100644 --- a/src/protectedApi_v8/discussionHub/topics.ts +++ b/src/protectedApi_v8/discussionHub/topics.ts @@ -5,16 +5,16 @@ import { axiosRequestConfig } from '../../configs/request.config' import { getUserUID, getWriteApiToken } from '../../utils/discussionHub-helper' import { CONSTANTS } from '../../utils/env' import { logError, logInfo } from '../../utils/logger' -import { extractUserIdFromRequest } from '../../utils/requestExtract' +import { extractUserIdFromRequest, extractUserToken } from '../../utils/requestExtract' const API_ENDPOINTS = { - getPopularTopics: `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/popular`, - getRecentTopics: `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/recent`, - getTopTopics: `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/top`, - getUnreadTopics: `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/unread`, - getUnreadTopicsTotal: `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/unread/total`, + getPopularTopics: `${CONSTANTS.KONG_API_BASE}/nodebb/api/popular`, + getRecentTopics: `${CONSTANTS.KONG_API_BASE}/nodebb/api/recent`, + getTopTopics: `${CONSTANTS.KONG_API_BASE}/nodebb/api/top`, + getUnreadTopics: `${CONSTANTS.KONG_API_BASE}/nodebb/auth/api/unread`, + getUnreadTopicsTotal: `${CONSTANTS.KONG_API_BASE}/nodebb/auth/api/unread/total`, // tslint:disable-next-line: object-literal-sort-keys - getTopicDetails: (tid: number) => `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/topic/${tid}`, + getTopicDetails: (tid: number) => `${CONSTANTS.KONG_API_BASE}/nodebb/auth/api/topic/${tid}`, } export const topicsApi = Router() @@ -31,7 +31,12 @@ topicsApi.get('/recent', async (req, res) => { logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}, Url: ${url}`) const response = await axios.get( url, - { ...axiosRequestConfig, headers: { rootOrg } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) res.send(response.data) } catch (err) { @@ -49,7 +54,12 @@ topicsApi.get('/top', async (req, res) => { const url = API_ENDPOINTS.getTopTopics const response = await axios.get( url, - { ...axiosRequestConfig, headers: { rootOrg } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) res.send(response.data) } catch (err) { @@ -68,7 +78,12 @@ topicsApi.get('/popular', async (req, res) => { const url = API_ENDPOINTS.getPopularTopics const response = await axios.get( `${url}?page=${pageNo}`, - { ...axiosRequestConfig, headers: { rootOrg } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) res.send(response.data) } catch (err) { @@ -87,7 +102,14 @@ topicsApi.get('/unread', async (req, res) => { const url = API_ENDPOINTS.getUnreadTopics + `?_uid=${userUid}` const response = await axios.get( url, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + }, + } ) res.send(response.data) } catch (err) { @@ -106,7 +128,13 @@ topicsApi.get('/unread/total', async (req, res) => { const url = API_ENDPOINTS.getUnreadTopicsTotal + `?_uid=${userUid}` const response = await axios.get( url, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) res.send(response.data) } catch (err) { @@ -128,7 +156,13 @@ topicsApi.get('/:tid', async (req, res) => { const url = API_ENDPOINTS.getTopicDetails(tid) + `?page=${pageNo}&_uid=${userUid}&sort=${sort}` const response = await axios.get( url, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) res.send(response.data) } catch (err) { diff --git a/src/protectedApi_v8/discussionHub/users.ts b/src/protectedApi_v8/discussionHub/users.ts index 828fb6c2..8bc7acf4 100644 --- a/src/protectedApi_v8/discussionHub/users.ts +++ b/src/protectedApi_v8/discussionHub/users.ts @@ -5,20 +5,20 @@ import { axiosRequestConfig } from '../../configs/request.config' import { getUserSlug, getUserUID, getWriteApiToken } from '../../utils/discussionHub-helper' import { CONSTANTS } from '../../utils/env' import { logError, logInfo } from '../../utils/logger' -import { extractUserIdFromRequest } from '../../utils/requestExtract' +import { extractUserIdFromRequest, extractUserToken} from '../../utils/requestExtract' const API_ENDPOINTS = { - getUserBookmarks: (slug: string) => `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/user/${slug}/bookmarks`, - getUserDownvotedPosts: (slug: string) => `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/user/${slug}/downvoted`, - getUserGroups: (slug: string) => `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/user/${slug}/groups`, - getUserInfo: (slug: string) => `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/user/${slug}/info`, - getUserPosts: (slug: string) => `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/user/${slug}/posts`, - getUserProfile: (slug: string) => `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/user/${slug}`, - getUserUpvotedPosts: (slug: string) => `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/user/${slug}/upvoted`, - getUsersWatchedTopics: (slug: string) => `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/user/${slug}/watched`, + getUserBookmarks: (slug: string) => `${CONSTANTS.KONG_API_BASE}/nodebb/auth/api/user/${slug}/bookmarks`, + getUserDownvotedPosts: (slug: string) => `${CONSTANTS.KONG_API_BASE}/nodebb/auth/api/user/${slug}/downvoted`, + getUserGroups: (slug: string) => `${CONSTANTS.KONG_API_BASE}/nodebb/auth/api/user/${slug}/groups`, + getUserInfo: (slug: string) => `${CONSTANTS.KONG_API_BASE}/nodebb/auth/api/user/${slug}/info`, + getUserPosts: (slug: string) => `${CONSTANTS.KONG_API_BASE}/nodebb/auth/api/user/${slug}/posts`, + getUserProfile: (slug: string) => `${CONSTANTS.KONG_API_BASE}/nodebb/auth/api/user/${slug}`, + getUserUpvotedPosts: (slug: string) => `${CONSTANTS.KONG_API_BASE}/nodebb/auth/api/user/${slug}/upvoted`, + getUsersWatchedTopics: (slug: string) => `${CONSTANTS.KONG_API_BASE}/nodebb/auth/api/user/${slug}/watched`, // tslint:disable-next-line: object-literal-sort-keys - getUserByEmail: (email: string) => `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/user/email/${email}`, - getUserByUsername: (username: string) => `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/user/username/${username}`, + getUserByEmail: (email: string) => `${CONSTANTS.KONG_API_BASE}/nodebb/api/user/email/${email}`, + getUserByUsername: (username: string) => `${CONSTANTS.KONG_API_BASE}/api/user/username/${username}`, } export const usersApi = Router() @@ -33,7 +33,13 @@ usersApi.get('/:slug/bookmarks', async (req, res) => { const url = API_ENDPOINTS.getUserBookmarks(slug) + `?_uid=${userUid}` const response = await axios.get( url, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) res.send(response.data) } catch (err) { @@ -53,7 +59,13 @@ usersApi.get('/:slug/downvoted', async (req, res) => { const url = API_ENDPOINTS.getUserDownvotedPosts(slug) + `?_uid=${userUid}` const response = await axios.get( url, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) res.send(response.data) } catch (err) { @@ -73,7 +85,13 @@ usersApi.get('/:slug/groups', async (req, res) => { const url = API_ENDPOINTS.getUserGroups(slug) + `?_uid=${userUid}` const response = await axios.get( url, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) res.send(response.data) } catch (err) { @@ -93,7 +111,13 @@ usersApi.get('/:slug/info', async (req, res) => { const url = API_ENDPOINTS.getUserInfo(slug) + `?_uid=${userUid}` const response = await axios.get( url, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) res.send(response.data) } catch (err) { @@ -113,7 +137,13 @@ usersApi.get('/me', async (req, res) => { const url = API_ENDPOINTS.getUserProfile(userSlug) + `?_uid=${userUid}` const response = await axios.get( url, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) res.send(response.data) } catch (err) { @@ -133,7 +163,13 @@ usersApi.get('/:slug/posts', async (req, res) => { const url = API_ENDPOINTS.getUserPosts(slug) + `?_uid=${userUid}` const response = await axios.get( url, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) res.send(response.data) } catch (err) { @@ -153,7 +189,13 @@ usersApi.get('/:slug/upvoted', async (req, res) => { const url = API_ENDPOINTS.getUserUpvotedPosts(slug) + `?_uid=${userUid}` const response = await axios.get( url, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) res.send(response.data) } catch (err) { @@ -173,7 +215,13 @@ usersApi.get('/:slug/watched', async (req, res) => { const url = API_ENDPOINTS.getUsersWatchedTopics(slug) + `?_uid=${userUid}` const response = await axios.get( url, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + rootOrg, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) res.send(response.data) } catch (err) { @@ -189,7 +237,7 @@ usersApi.get('/email/:email', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const email = req.params.email - const response = await getUserByEmail(email) + const response = await getUserByEmail(req, email) res.send(response.data) } catch (err) { logError('ERROR ON GET topicsApi /email/:email >', err) @@ -210,7 +258,12 @@ usersApi.get('/:slug/about', async (req, res) => { logInfo('called /:slug/about url=> ', url) const response = await axios.get( url, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) res.send(response.data) } catch (err) { @@ -221,7 +274,7 @@ usersApi.get('/:slug/about', async (req, res) => { }) // tslint:disable-next-line: no-any -export async function getUserByEmail(email: any): Promise { +export async function getUserByEmail(req:any , email: any): Promise { logInfo('Finding user in NodeBB DiscussionHub...') // tslint:disable-next-line: no-try-promise try { @@ -229,7 +282,11 @@ export async function getUserByEmail(email: any): Promise { return new Promise(async (resolve, reject) => { const response = await axios.get( url, - { ...axiosRequestConfig } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + }} ).catch((err) => { logError('ERROR ON method getUserByEmail api call to nodebb DiscussionHub >', err) reject(err) @@ -244,7 +301,7 @@ export async function getUserByEmail(email: any): Promise { } // tslint:disable-next-line: no-any -export async function getUserByUsername(username: any): Promise { +export async function getUserByUsername(req : any , username: any): Promise { logInfo('Finding user in NodeBB DiscussionHub...') // tslint:disable-next-line: no-try-promise try { @@ -252,7 +309,11 @@ export async function getUserByUsername(username: any): Promise { return new Promise(async (resolve, reject) => { const response = await axios.get( url, - { ...axiosRequestConfig } + { ...axiosRequestConfig , headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + }} ).catch((err) => { logError('ERROR ON method getUserByUsername api call to nodebb DiscussionHub >', err) reject(err) diff --git a/src/protectedApi_v8/discussionHub/writeApi.ts b/src/protectedApi_v8/discussionHub/writeApi.ts index 4eab0086..13ccf213 100644 --- a/src/protectedApi_v8/discussionHub/writeApi.ts +++ b/src/protectedApi_v8/discussionHub/writeApi.ts @@ -5,25 +5,25 @@ import { axiosRequestConfig } from '../../configs/request.config' import { getUserUID, getWriteApiAdminUID, getWriteApiToken } from '../../utils/discussionHub-helper' import { CONSTANTS } from '../../utils/env' import { logError, logInfo } from '../../utils/logger' -import { extractUserIdFromRequest } from '../../utils/requestExtract' +import { extractUserIdFromRequest, extractUserToken } from '../../utils/requestExtract' const API_ENDPOINTS = { - createTopic: `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/v2/topics`, - createUser: `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/v2/users`, + createTopic: `${CONSTANTS.KONG_API_BASE}/nodebb/auth/api/v2/topics`, + createUser: `${CONSTANTS.KONG_API_BASE}/nodebb/auth/api/v2/users`, // tslint:disable-next-line: object-literal-sort-keys createOrUpdateTags: (topicId: string | number) => - `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/v2/topics/${topicId}/tags`, - followTopic: (topicId: string | number) => `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/v2/topics/${topicId}/follow`, - replyToTopic: (topicId: string | number) => `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/v2/topics/${topicId}`, - votePost: (postId: string | number) => `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/v2/posts/${postId}/vote`, + `${CONSTANTS.KONG_API_BASE}/nodebb/auth/api/v2/topics/${topicId}/tags`, + followTopic: (topicId: string | number) => `${CONSTANTS.KONG_API_BASE}/nodebb/auth/api/v2/topics/${topicId}/follow`, + replyToTopic: (topicId: string | number) => `${CONSTANTS.KONG_API_BASE}/nodebb/auth/api/v2/topics/${topicId}`, + votePost: (postId: string | number) => `${CONSTANTS.KONG_API_BASE}/nodebb/auth/api/v2/posts/${postId}/vote`, // tslint:disable-next-line: object-literal-sort-keys - bookmarkPost: (postId: string | number) => `${CONSTANTS.DISCUSSION_HUB_API_BASE}/api/v2/posts/${postId}/bookmark`, + bookmarkPost: (postId: string | number) => `${CONSTANTS.KONG_API_BASE}/nodebb/auth/api/v2/posts/${postId}/bookmark`, } export const writeApi = Router() // tslint:disable-next-line: no-any -export async function createDiscussionHubUser(user: any): Promise { +export async function createDiscussionHubUser(req: any , user: any): Promise { logInfo('Starting to create new user into NodeBB DiscussionHub...') // tslint:disable-next-line: no-try-promise try { @@ -36,7 +36,12 @@ export async function createDiscussionHubUser(user: any): Promise { const response = await axios.post( url, request1, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ).catch((err) => { logError('ERROR ON method createDiscussionHubUser api call to nodebb DiscussionHub>', err) reject(err) @@ -63,7 +68,12 @@ writeApi.post('/topics', async (req, res) => { ...req.body, _uid: userUid, }, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) if (response && response.data) { res.send(response.data) @@ -89,7 +99,12 @@ writeApi.post('/topics/:topicId', async (req, res) => { ...req.body, _uid: userUid, }, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) if (response && response.data) { res.send(response.data) @@ -106,7 +121,7 @@ writeApi.post('/users', async (req, res) => { const rootOrg = getRootOrg(req) const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) - const response = await createDiscussionHubUser(req.body) + const response = await createDiscussionHubUser(req, req.body) res.send(response.data) } catch (err) { logError('ERROR ON writeAPI POST /users >', err) @@ -128,7 +143,12 @@ writeApi.post('/posts/:postId/bookmark', async (req, res) => { { _uid: userUid, }, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) if (response && response.data) { res.send(response.data) @@ -150,7 +170,12 @@ writeApi.delete('/posts/:postId/bookmark', async (req, res) => { const url = API_ENDPOINTS.bookmarkPost(postId) + `?_uid=${userUid}` const response = await axios.delete( url, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) if (response && response.data) { res.send(response.data) @@ -176,7 +201,12 @@ writeApi.post('/posts/:postId/vote', async (req, res) => { ...req.body, _uid: userUid, }, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) if (response && response.data) { res.send(response.data) @@ -198,7 +228,12 @@ writeApi.delete('/posts/:postId/vote', async (req, res) => { const url = API_ENDPOINTS.votePost(postId) + `?_uid=${userUid}` const response = await axios.delete( url, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) if (response && response.data) { res.send(response.data) @@ -224,7 +259,12 @@ writeApi.put('/topics/:topicId/follow', async (req, res) => { // TODO : _uid: userUid, }, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) if (response && response.data) { res.send(response.data) @@ -248,7 +288,12 @@ writeApi.put('/topics/:topicId/tags', async (req, res) => { { ...req.body, }, - { ...axiosRequestConfig, headers: { authorization: getWriteApiToken() } } + { ...axiosRequestConfig, headers: { + Authorization: CONSTANTS.SB_API_KEY, + nodebb_authorization_token: getWriteApiToken(), + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req) + } } ) if (response && response.data) { res.send(response.data) From e940c4d93ea03880412733f4972ce71760d71cdd Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Thu, 12 Aug 2021 21:01:38 +0530 Subject: [PATCH 121/856] Discussion hub api kong related changes --- .../discussionHub/notifications.ts | 2 +- src/protectedApi_v8/discussionHub/topics.ts | 6 +-- src/protectedApi_v8/discussionHub/users.ts | 40 +++++++++---------- src/protectedApi_v8/discussionHub/writeApi.ts | 14 +++---- src/protectedApi_v8/user/details.ts | 4 +- src/utils/discussionHub-helper.ts | 8 ++-- 6 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/protectedApi_v8/discussionHub/notifications.ts b/src/protectedApi_v8/discussionHub/notifications.ts index 5b6b278b..8316a1e1 100644 --- a/src/protectedApi_v8/discussionHub/notifications.ts +++ b/src/protectedApi_v8/discussionHub/notifications.ts @@ -18,7 +18,7 @@ notificationsApi.get('/', async (req, res) => { const rootOrg = getRootOrg(req) const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) - const userUid = await getUserUID(userId) + const userUid = await getUserUID(req, userId) const url = API_ENDPOINTS.getNotifications + `?_uid=${userUid}` const response = await axios.get( url, diff --git a/src/protectedApi_v8/discussionHub/topics.ts b/src/protectedApi_v8/discussionHub/topics.ts index f63051ad..731ee7a7 100644 --- a/src/protectedApi_v8/discussionHub/topics.ts +++ b/src/protectedApi_v8/discussionHub/topics.ts @@ -98,7 +98,7 @@ topicsApi.get('/unread', async (req, res) => { const rootOrg = getRootOrg(req) const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) - const userUid = await getUserUID(userId) + const userUid = await getUserUID(req,userId) const url = API_ENDPOINTS.getUnreadTopics + `?_uid=${userUid}` const response = await axios.get( url, @@ -124,7 +124,7 @@ topicsApi.get('/unread/total', async (req, res) => { const rootOrg = getRootOrg(req) const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) - const userUid = await getUserUID(userId) + const userUid = await getUserUID(req,userId) const url = API_ENDPOINTS.getUnreadTopicsTotal + `?_uid=${userUid}` const response = await axios.get( url, @@ -152,7 +152,7 @@ topicsApi.get('/:tid', async (req, res) => { const sort = req.query.sort || '' logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const tid = req.params.tid - const userUid = await getUserUID(userId) + const userUid = await getUserUID(req, userId) const url = API_ENDPOINTS.getTopicDetails(tid) + `?page=${pageNo}&_uid=${userUid}&sort=${sort}` const response = await axios.get( url, diff --git a/src/protectedApi_v8/discussionHub/users.ts b/src/protectedApi_v8/discussionHub/users.ts index 8bc7acf4..ee91d9a5 100644 --- a/src/protectedApi_v8/discussionHub/users.ts +++ b/src/protectedApi_v8/discussionHub/users.ts @@ -29,11 +29,11 @@ usersApi.get('/:slug/bookmarks', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const slug = req.params.slug - const userUid = await getUserUID(userId) + const userUid = await getUserUID(req, userId) const url = API_ENDPOINTS.getUserBookmarks(slug) + `?_uid=${userUid}` const response = await axios.get( url, - { ...axiosRequestConfig, headers: { + { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, nodebb_authorization_token: getWriteApiToken(), rootOrg, @@ -55,11 +55,11 @@ usersApi.get('/:slug/downvoted', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const slug = req.params.slug - const userUid = await getUserUID(userId) + const userUid = await getUserUID(req, userId) const url = API_ENDPOINTS.getUserDownvotedPosts(slug) + `?_uid=${userUid}` const response = await axios.get( url, - { ...axiosRequestConfig, headers: { + { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, nodebb_authorization_token: getWriteApiToken(), rootOrg, @@ -81,11 +81,11 @@ usersApi.get('/:slug/groups', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const slug = req.params.slug - const userUid = await getUserUID(userId) + const userUid = await getUserUID(req, userId) const url = API_ENDPOINTS.getUserGroups(slug) + `?_uid=${userUid}` const response = await axios.get( url, - { ...axiosRequestConfig, headers: { + { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, nodebb_authorization_token: getWriteApiToken(), rootOrg, @@ -107,11 +107,11 @@ usersApi.get('/:slug/info', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const slug = req.params.slug - const userUid = await getUserUID(userId) + const userUid = await getUserUID(req, userId) const url = API_ENDPOINTS.getUserInfo(slug) + `?_uid=${userUid}` const response = await axios.get( url, - { ...axiosRequestConfig, headers: { + { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, nodebb_authorization_token: getWriteApiToken(), rootOrg, @@ -132,8 +132,8 @@ usersApi.get('/me', async (req, res) => { const rootOrg = getRootOrg(req) const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) - const userSlug = await getUserSlug(userId) - const userUid = await getUserUID(userId) + const userSlug = await getUserSlug(req, userId) + const userUid = await getUserUID(req, userId) const url = API_ENDPOINTS.getUserProfile(userSlug) + `?_uid=${userUid}` const response = await axios.get( url, @@ -159,11 +159,11 @@ usersApi.get('/:slug/posts', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const slug = req.params.slug - const userUid = await getUserUID(userId) + const userUid = await getUserUID(req, userId) const url = API_ENDPOINTS.getUserPosts(slug) + `?_uid=${userUid}` const response = await axios.get( url, - { ...axiosRequestConfig, headers: { + { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, nodebb_authorization_token: getWriteApiToken(), rootOrg, @@ -185,11 +185,11 @@ usersApi.get('/:slug/upvoted', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const slug = req.params.slug - const userUid = await getUserUID(userId) + const userUid = await getUserUID(req, userId) const url = API_ENDPOINTS.getUserUpvotedPosts(slug) + `?_uid=${userUid}` const response = await axios.get( url, - { ...axiosRequestConfig, headers: { + { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, nodebb_authorization_token: getWriteApiToken(), rootOrg, @@ -211,11 +211,11 @@ usersApi.get('/:slug/watched', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const slug = req.params.slug - const userUid = await getUserUID(userId) + const userUid = await getUserUID(req, userId) const url = API_ENDPOINTS.getUsersWatchedTopics(slug) + `?_uid=${userUid}` const response = await axios.get( url, - { ...axiosRequestConfig, headers: { + { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, nodebb_authorization_token: getWriteApiToken(), rootOrg, @@ -252,13 +252,13 @@ usersApi.get('/:slug/about', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const slug = req.params.slug - const userUid = await getUserUID(userId) + const userUid = await getUserUID(req, userId) logInfo('called /:slug/about slug=> ', slug) const url = API_ENDPOINTS.getUserProfile(slug) + `?_uid=${userUid}` logInfo('called /:slug/about url=> ', url) const response = await axios.get( url, - { ...axiosRequestConfig, headers: { + { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, nodebb_authorization_token: getWriteApiToken(), // tslint:disable-next-line: all @@ -274,7 +274,7 @@ usersApi.get('/:slug/about', async (req, res) => { }) // tslint:disable-next-line: no-any -export async function getUserByEmail(req:any , email: any): Promise { +export async function getUserByEmail(req: any , email: any): Promise { logInfo('Finding user in NodeBB DiscussionHub...') // tslint:disable-next-line: no-try-promise try { @@ -301,7 +301,7 @@ export async function getUserByEmail(req:any , email: any): Promise { } // tslint:disable-next-line: no-any -export async function getUserByUsername(req : any , username: any): Promise { +export async function getUserByUsername(req: any , username: any): Promise { logInfo('Finding user in NodeBB DiscussionHub...') // tslint:disable-next-line: no-try-promise try { diff --git a/src/protectedApi_v8/discussionHub/writeApi.ts b/src/protectedApi_v8/discussionHub/writeApi.ts index 13ccf213..6054275e 100644 --- a/src/protectedApi_v8/discussionHub/writeApi.ts +++ b/src/protectedApi_v8/discussionHub/writeApi.ts @@ -61,7 +61,7 @@ writeApi.post('/topics', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const url = API_ENDPOINTS.createTopic - const userUid = await getUserUID(userId) + const userUid = await getUserUID(req, userId) const response = await axios.post( url, { @@ -92,7 +92,7 @@ writeApi.post('/topics/:topicId', async (req, res) => { logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const topicId = req.params.topicId const url = API_ENDPOINTS.replyToTopic(topicId) - const userUid = await getUserUID(userId) + const userUid = await getUserUID(req, userId) const response = await axios.post( url, { @@ -137,7 +137,7 @@ writeApi.post('/posts/:postId/bookmark', async (req, res) => { logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const postId = req.params.postId const url = API_ENDPOINTS.bookmarkPost(postId) - const userUid = await getUserUID(userId) + const userUid = await getUserUID(req, userId) const response = await axios.post( url, { @@ -166,7 +166,7 @@ writeApi.delete('/posts/:postId/bookmark', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const postId = req.params.postId - const userUid = await getUserUID(userId) + const userUid = await getUserUID(req, userId) const url = API_ENDPOINTS.bookmarkPost(postId) + `?_uid=${userUid}` const response = await axios.delete( url, @@ -194,7 +194,7 @@ writeApi.post('/posts/:postId/vote', async (req, res) => { logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const postId = req.params.postId const url = API_ENDPOINTS.votePost(postId) - const userUid = await getUserUID(userId) + const userUid = await getUserUID(req, userId) const response = await axios.post( url, { @@ -224,7 +224,7 @@ writeApi.delete('/posts/:postId/vote', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const postId = req.params.postId - const userUid = await getUserUID(userId) + const userUid = await getUserUID(req, userId) const url = API_ENDPOINTS.votePost(postId) + `?_uid=${userUid}` const response = await axios.delete( url, @@ -252,7 +252,7 @@ writeApi.put('/topics/:topicId/follow', async (req, res) => { logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const topicId = req.params.topicId const url = API_ENDPOINTS.followTopic(topicId) - const userUid = await getUserUID(userId) + const userUid = await getUserUID(req, userId) const response = await axios.put( url, { diff --git a/src/protectedApi_v8/user/details.ts b/src/protectedApi_v8/user/details.ts index 190fd33d..71f593df 100644 --- a/src/protectedApi_v8/user/details.ts +++ b/src/protectedApi_v8/user/details.ts @@ -149,7 +149,7 @@ export function wTokenApiMock(req: any, token: any): Promise { const user = body.user // Check if user is present in NodeBB DiscussionHub // tslint:disable-next-line: no-identical-functions - const userPresent = await getUserByEmail(user.email).catch(async (err) => { + const userPresent = await getUserByEmail(req, user.email).catch(async (err) => { if (err.response && (err.response.status === 404)) { // If user is not already present in nodeBB DiscussionHub // then create the user @@ -160,7 +160,7 @@ export function wTokenApiMock(req: any, token: any): Promise { username: user.wid, } // tslint:disable-next-line: no-any - await createDiscussionHubUser(reqToDiscussionHub).catch((createDiscussionHubUserErr: any) => { + await createDiscussionHubUser(req, reqToDiscussionHub).catch((createDiscussionHubUserErr: any) => { logError(`Creatin of User failed..!:`, createDiscussionHubUserErr) resolve(body) }) diff --git a/src/utils/discussionHub-helper.ts b/src/utils/discussionHub-helper.ts index b88f3a95..2bef841c 100644 --- a/src/utils/discussionHub-helper.ts +++ b/src/utils/discussionHub-helper.ts @@ -21,9 +21,9 @@ export function getWriteApiAdminUID(): number { } // tslint:disable-next-line: no-any -export async function getUserUID(wid: any) { +export async function getUserUID(req: any, wid: any) { // tslint:disable-next-line: no-any - const userPresent = await getUserByUsername(wid).catch(async (_err: any) => { + const userPresent = await getUserByUsername(req, wid).catch(async (_err: any) => { // If user is not already present in nodeBB NodeBB DiscussionHub logError(usrNotFound) return Promise.reject(new Error(usrNotFound)) @@ -35,9 +35,9 @@ export async function getUserUID(wid: any) { } // tslint:disable-next-line: no-any -export async function getUserSlug(wid: any) { +export async function getUserSlug(req: any, wid: any) { // tslint:disable-next-line: no-any - const userPresent = await getUserByUsername(wid).catch(async (_err: any) => { + const userPresent = await getUserByUsername(req, wid).catch(async (_err: any) => { // If user is not already present in nodeBB NodeBB DiscussionHub logError(usrNotFound) return Promise.reject(new Error('User not found')) From b03dd28a01b45b2e778301ef8561b521bf64b635 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Mon, 16 Aug 2021 13:25:02 +0530 Subject: [PATCH 122/856] Added api to whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 42cef818..4bf190fa 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -464,6 +464,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/discussion/v2/posts/:id/bookmark': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/learnervm/private/content/v3/retire/': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1164,6 +1171,7 @@ export const API_LIST = { '/proxies/v8/discussion/topic/:id/:slug', '/proxies/v8/discussion/v2/posts/:id/vote', '/proxies/v8/discussion/v2/posts/:id', + '/proxies/v8/discussion/v2/posts/:id/bookmark', '/proxies/v8/learner/course/v1/user/enrollment/list/:id', '/proxies/v8/read/content-progres/:do_id', '/proxies/v8/content-progres/:do_id', From b73d496ff2ef31e4e96696a07d8e2a80962bb3e1 Mon Sep 17 00:00:00 2001 From: pilliarunkumar Date: Mon, 16 Aug 2021 15:36:17 +0530 Subject: [PATCH 123/856] Added api to whitelist --- src/utils/whitelistApis.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 4bf190fa..a489d8dd 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -471,6 +471,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/discussion/popular': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/learnervm/private/content/v3/retire/': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys From 434931efff403a798c4f2f6f8696974e91711317 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Tue, 17 Aug 2021 14:38:39 +0530 Subject: [PATCH 124/856] Added apis to whitelist --- src/utils/whitelistApis.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index a489d8dd..4dd10aa2 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -86,6 +86,27 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/api/event/v4/read': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/api/event/v4/publish': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/api/event/v4/create': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/user/v1/read/:id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1168,6 +1189,9 @@ export const API_LIST = { '/authApi/content/v3/update/:do_id', '/proxies/v8/api/user/v2/read', '/proxies/v8/api/user/v2/read/:id', + '/proxies/v8/api/event/v4/read', + '/proxies/v8/api/event/v4/publish', + '/proxies/v8/api/event/v4/create', '/proxies/v8/user/v1/read/:id', '/proxies/v8/sunbirdigot/read', '/proxies/v8/sunbirdigot/search', @@ -1198,6 +1222,7 @@ export const API_LIST = { '/proxies/v8/action/content/v3/updateReviewStatus/:do_id', '/proxies/v8/action/content/v3/review/:do_id', '/proxies/v8/discussion/recent', + '/proxies/v8/discussion/popular', '/proxies/v8/action/content/v3/publish/:do_id', '/proxies/v8/action/content/v3/reject/:do_id', '/proxies/v8/user/private/v1/assign/role', From f9aa91e3330683c539b6088cd08a55412e4059ba Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Tue, 17 Aug 2021 15:12:48 +0530 Subject: [PATCH 125/856] Changes in whitelist apis --- src/utils/whitelistApis.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 4dd10aa2..d9056e46 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -86,14 +86,14 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/proxies/v8/api/event/v4/read': { + '/proxies/v8/api/event/v4/read/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/proxies/v8/api/event/v4/publish': { + '/proxies/v8/api/event/v4/publish/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -1189,8 +1189,8 @@ export const API_LIST = { '/authApi/content/v3/update/:do_id', '/proxies/v8/api/user/v2/read', '/proxies/v8/api/user/v2/read/:id', - '/proxies/v8/api/event/v4/read', - '/proxies/v8/api/event/v4/publish', + '/proxies/v8/api/event/v4/read/:do_id', + '/proxies/v8/api/event/v4/publish/:do_id', '/proxies/v8/api/event/v4/create', '/proxies/v8/user/v1/read/:id', '/proxies/v8/sunbirdigot/read', From 78188d86a2d0a7fc28f181636bd462ec64e68f0e Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Tue, 17 Aug 2021 16:21:54 +0530 Subject: [PATCH 126/856] Added Event handling in Proxy API --- src/proxies_v8/proxies_v8.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index aef68631..363d4ea5 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -222,6 +222,10 @@ proxiesV8.use('/user/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) +proxiesV8.use('/event/*', + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + // proxiesV8.use('/api/framework/*', // // tslint:disable-next-line: max-line-length // proxyCreatorQML(express.Router(), `${CONSTANTS.KONG_API_BASE}`, '/api/') From 08d3ce45944ffae53f8e819458fddb06d24b423d Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Tue, 17 Aug 2021 16:24:01 +0530 Subject: [PATCH 127/856] Updated the path for Events --- src/utils/whitelistApis.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index d9056e46..4902c153 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -86,21 +86,21 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/proxies/v8/api/event/v4/read/:do_id': { + '/proxies/v8/event/v4/read/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/proxies/v8/api/event/v4/publish/:do_id': { + '/proxies/v8/event/v4/publish/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/proxies/v8/api/event/v4/create': { + '/proxies/v8/event/v4/create': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -1189,9 +1189,9 @@ export const API_LIST = { '/authApi/content/v3/update/:do_id', '/proxies/v8/api/user/v2/read', '/proxies/v8/api/user/v2/read/:id', - '/proxies/v8/api/event/v4/read/:do_id', - '/proxies/v8/api/event/v4/publish/:do_id', - '/proxies/v8/api/event/v4/create', + '/proxies/v8/event/v4/read/:do_id', + '/proxies/v8/event/v4/publish/:do_id', + '/proxies/v8/event/v4/create', '/proxies/v8/user/v1/read/:id', '/proxies/v8/sunbirdigot/read', '/proxies/v8/sunbirdigot/search', From 9239313cbe735d54febf7b06f18ce0f4f6f9d0b6 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Wed, 18 Aug 2021 13:13:04 +0530 Subject: [PATCH 128/856] Added api to whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 4902c153..4423b5f8 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -513,6 +513,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/notifyContentState': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/connections/connections/recommended/userDepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1199,6 +1206,7 @@ export const API_LIST = { '/proxies/v8/data/v1/system/settings/get/orgTypeList', '/proxies/v8/org/v1/search', '/proxies/v8/org/v1/update', + '/proxies/v8/notifyContentState', '/proxies/v8/discussion/topic/:id/:slug', '/proxies/v8/discussion/v2/posts/:id/vote', '/proxies/v8/discussion/v2/posts/:id', From d617fbccd87e5b637ddc503706601fad6b4a87f6 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Thu, 19 Aug 2021 13:24:55 +0530 Subject: [PATCH 129/856] Added log --- src/utils/permissionHelper.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/permissionHelper.ts b/src/utils/permissionHelper.ts index 76df6e8d..4f43efbd 100644 --- a/src/utils/permissionHelper.ts +++ b/src/utils/permissionHelper.ts @@ -1,12 +1,14 @@ const _ = require('lodash') import request from 'request' import { CONSTANTS } from './env' +import { logInfo } from './logger' export const PERMISSION_HELPER = { // tslint:disable-next-line: no-any setRolesData(reqObj: any, callback: any, body: any) { // tslint:disable-next-line: no-any const userData: any = JSON.parse(body) + logInfo(JSON.stringify(userData)) if (reqObj.session) { reqObj.session.userId = userData.result.response.id ? userData.result.response.id : userData.result.response.userId reqObj.session.userName = userData.result.response.userName From 0f607b73873c96f75327e9fedd15adb02fb0199f Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Thu, 19 Aug 2021 13:51:31 +0530 Subject: [PATCH 130/856] Base url change --- src/utils/permissionHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/permissionHelper.ts b/src/utils/permissionHelper.ts index 4f43efbd..952dd976 100644 --- a/src/utils/permissionHelper.ts +++ b/src/utils/permissionHelper.ts @@ -33,7 +33,7 @@ export const PERMISSION_HELPER = { // console.log('Step 3: Get user roles function') const userId = reqObj.session.userId // console.log(userId) - const readUrl = `${CONSTANTS.SUNBIRD_PROXY_API_BASE}/user/v2/read/` + userId + const readUrl = `${CONSTANTS.KONG_API_BASE}/user/v2/read/` + userId const options = { headers: { Authorization: CONSTANTS.SB_API_KEY, From 8bc9614cac3615b9ad2cdab0b7811eb7de56422f Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Thu, 19 Aug 2021 15:11:02 +0530 Subject: [PATCH 131/856] content email changes --- src/proxies_v8/proxies_v8.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 363d4ea5..c15150da 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -313,6 +313,8 @@ proxiesV8.post('/notifyContentState', async (req, res) => { id: `${CONSTANTS.NOTIFY_EMAIL_TEMPLATE_ID}`, params: { body: contentBody, + orgImageUrl: `${CONSTANTS.FRAC_API_BASE}` + '/img/logos/iGOT_logo.png', + orgName: 'iGOT Support Team', }, }, } From 40b7ab67aadea0eafa850efb80e29e224e97bfa9 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Thu, 19 Aug 2021 15:17:08 +0530 Subject: [PATCH 132/856] Changed contentEmailTemplate --- src/utils/env.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/env.ts b/src/utils/env.ts index c01de611..c4f22b3b 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -148,7 +148,7 @@ export const CONSTANTS = { NOTIFY_PUBLIST_FAILED: 'The content #contentLink which sent for publish requires few more changes. Please contact the publishers.', NOTIFY_PUBLISH_COMPLETED_BODY: 'The content #contentLink is successfully published.' + ' The content will be available for the users in few hours.', - NOTIFY_EMAIL_TEMPLATE_ID: 'emailtemplate', + NOTIFY_EMAIL_TEMPLATE_ID: 'contentEmailTemplate', CONTENT_SERVICE_API_BASE: env.CONTENT_SERVICE_API_BASE || 'http://content-service:9000', VM_LEARNING_SERVICE_URL: env.VM_LEARNING_SERVICE_URL, // tslint:disable-next-line: max-line-length From 3095ab6f346f466733b40130288c384cb4d777d0 Mon Sep 17 00:00:00 2001 From: Amit Kumar Yadav <36166686+amit-kumaryadav@users.noreply.github.com> Date: Mon, 23 Aug 2021 11:38:12 +0530 Subject: [PATCH 133/856] Update requestExtract.ts --- src/utils/requestExtract.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/utils/requestExtract.ts b/src/utils/requestExtract.ts index 7980a37c..c161f177 100644 --- a/src/utils/requestExtract.ts +++ b/src/utils/requestExtract.ts @@ -29,15 +29,10 @@ export const extractUserIdFromRequest = (req: IAuthorizedRequest): string => { export const extractUserId = (req: IAuthorizedRequest): string => { const wid = req.header('wid') if (wid) { - // tslint:disable-next-line: no-console - console.log('------------------- user id -------', wid) return wid } const userId = (req.kauth && req.kauth.grant.access_token.content.sub) as string - const returnUserId = userId.split(':')[2] - // tslint:disable-next-line: no-console - console.log('------------------- returnUserId -------', returnUserId) - return returnUserId + return userId.split(':')[2] } export const extractUserNameFromRequest = (req: IAuthorizedRequest) => From 7e44a0b4fa87f1389d7ac31f46e4fb4ca4eb8190 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Wed, 25 Aug 2021 14:56:30 +0530 Subject: [PATCH 134/856] Discussion Hub API changes --- .../discussionHub/notifications.ts | 5 ++-- src/protectedApi_v8/discussionHub/topics.ts | 11 +++---- src/protectedApi_v8/discussionHub/users.ts | 29 +++++++------------ src/protectedApi_v8/discussionHub/writeApi.ts | 25 +++++----------- src/utils/discussionHub-helper.ts | 8 ++++- 5 files changed, 31 insertions(+), 47 deletions(-) diff --git a/src/protectedApi_v8/discussionHub/notifications.ts b/src/protectedApi_v8/discussionHub/notifications.ts index 8316a1e1..bc8ca5ba 100644 --- a/src/protectedApi_v8/discussionHub/notifications.ts +++ b/src/protectedApi_v8/discussionHub/notifications.ts @@ -2,7 +2,7 @@ import axios from 'axios' import { Router } from 'express' import { getRootOrg } from '../../authoring/utils/header' import { axiosRequestConfig } from '../../configs/request.config' -import { getUserUID, getWriteApiToken } from '../../utils/discussionHub-helper' +import { getUserUIDBySession, getWriteApiToken } from '../../utils/discussionHub-helper' import { CONSTANTS } from '../../utils/env' import { logError, logInfo } from '../../utils/logger' import { extractUserIdFromRequest , extractUserToken} from '../../utils/requestExtract' @@ -18,13 +18,12 @@ notificationsApi.get('/', async (req, res) => { const rootOrg = getRootOrg(req) const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) - const userUid = await getUserUID(req, userId) + const userUid = await getUserUIDBySession(req) const url = API_ENDPOINTS.getNotifications + `?_uid=${userUid}` const response = await axios.get( url, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, - nodebb_authorization_token: getWriteApiToken(), // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req) } } diff --git a/src/protectedApi_v8/discussionHub/topics.ts b/src/protectedApi_v8/discussionHub/topics.ts index 731ee7a7..2f6b3962 100644 --- a/src/protectedApi_v8/discussionHub/topics.ts +++ b/src/protectedApi_v8/discussionHub/topics.ts @@ -2,7 +2,7 @@ import axios from 'axios' import { Router } from 'express' import { getRootOrg } from '../../authoring/utils/header' import { axiosRequestConfig } from '../../configs/request.config' -import { getUserUID, getWriteApiToken } from '../../utils/discussionHub-helper' +import { getUserUIDBySession, getWriteApiToken } from '../../utils/discussionHub-helper' import { CONSTANTS } from '../../utils/env' import { logError, logInfo } from '../../utils/logger' import { extractUserIdFromRequest, extractUserToken } from '../../utils/requestExtract' @@ -98,13 +98,12 @@ topicsApi.get('/unread', async (req, res) => { const rootOrg = getRootOrg(req) const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) - const userUid = await getUserUID(req,userId) + const userUid = await getUserUIDBySession(req) const url = API_ENDPOINTS.getUnreadTopics + `?_uid=${userUid}` const response = await axios.get( url, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, - nodebb_authorization_token: getWriteApiToken(), rootOrg, // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req) @@ -124,13 +123,12 @@ topicsApi.get('/unread/total', async (req, res) => { const rootOrg = getRootOrg(req) const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) - const userUid = await getUserUID(req,userId) + const userUid = await getUserUIDBySession(req) const url = API_ENDPOINTS.getUnreadTopicsTotal + `?_uid=${userUid}` const response = await axios.get( url, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, - nodebb_authorization_token: getWriteApiToken(), rootOrg, // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req) @@ -152,13 +150,12 @@ topicsApi.get('/:tid', async (req, res) => { const sort = req.query.sort || '' logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const tid = req.params.tid - const userUid = await getUserUID(req, userId) + const userUid = await getUserUIDBySession(req) const url = API_ENDPOINTS.getTopicDetails(tid) + `?page=${pageNo}&_uid=${userUid}&sort=${sort}` const response = await axios.get( url, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, - nodebb_authorization_token: getWriteApiToken(), rootOrg, // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req) diff --git a/src/protectedApi_v8/discussionHub/users.ts b/src/protectedApi_v8/discussionHub/users.ts index ee91d9a5..0497bea6 100644 --- a/src/protectedApi_v8/discussionHub/users.ts +++ b/src/protectedApi_v8/discussionHub/users.ts @@ -2,7 +2,7 @@ import axios from 'axios' import { Router } from 'express' import { getRootOrg } from '../../authoring/utils/header' import { axiosRequestConfig } from '../../configs/request.config' -import { getUserSlug, getUserUID, getWriteApiToken } from '../../utils/discussionHub-helper' +import { getUserSlug, getUserUIDBySession, getWriteApiToken } from '../../utils/discussionHub-helper' import { CONSTANTS } from '../../utils/env' import { logError, logInfo } from '../../utils/logger' import { extractUserIdFromRequest, extractUserToken} from '../../utils/requestExtract' @@ -29,13 +29,12 @@ usersApi.get('/:slug/bookmarks', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const slug = req.params.slug - const userUid = await getUserUID(req, userId) + const userUid = await getUserUIDBySession(req) const url = API_ENDPOINTS.getUserBookmarks(slug) + `?_uid=${userUid}` const response = await axios.get( url, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, - nodebb_authorization_token: getWriteApiToken(), rootOrg, // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req) @@ -55,13 +54,12 @@ usersApi.get('/:slug/downvoted', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const slug = req.params.slug - const userUid = await getUserUID(req, userId) + const userUid = await getUserUIDBySession(req) const url = API_ENDPOINTS.getUserDownvotedPosts(slug) + `?_uid=${userUid}` const response = await axios.get( url, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, - nodebb_authorization_token: getWriteApiToken(), rootOrg, // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req) @@ -81,13 +79,12 @@ usersApi.get('/:slug/groups', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const slug = req.params.slug - const userUid = await getUserUID(req, userId) + const userUid = await getUserUIDBySession(req) const url = API_ENDPOINTS.getUserGroups(slug) + `?_uid=${userUid}` const response = await axios.get( url, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, - nodebb_authorization_token: getWriteApiToken(), rootOrg, // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req) @@ -107,13 +104,12 @@ usersApi.get('/:slug/info', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const slug = req.params.slug - const userUid = await getUserUID(req, userId) + const userUid = await getUserUIDBySession(req) const url = API_ENDPOINTS.getUserInfo(slug) + `?_uid=${userUid}` const response = await axios.get( url, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, - nodebb_authorization_token: getWriteApiToken(), rootOrg, // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req) @@ -133,13 +129,12 @@ usersApi.get('/me', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const userSlug = await getUserSlug(req, userId) - const userUid = await getUserUID(req, userId) + const userUid = await getUserUIDBySession(req) const url = API_ENDPOINTS.getUserProfile(userSlug) + `?_uid=${userUid}` const response = await axios.get( url, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, - nodebb_authorization_token: getWriteApiToken(), rootOrg, // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req) @@ -159,13 +154,12 @@ usersApi.get('/:slug/posts', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const slug = req.params.slug - const userUid = await getUserUID(req, userId) + const userUid = await getUserUIDBySession(req) const url = API_ENDPOINTS.getUserPosts(slug) + `?_uid=${userUid}` const response = await axios.get( url, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, - nodebb_authorization_token: getWriteApiToken(), rootOrg, // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req) @@ -185,13 +179,12 @@ usersApi.get('/:slug/upvoted', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const slug = req.params.slug - const userUid = await getUserUID(req, userId) + const userUid = await getUserUIDBySession(req) const url = API_ENDPOINTS.getUserUpvotedPosts(slug) + `?_uid=${userUid}` const response = await axios.get( url, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, - nodebb_authorization_token: getWriteApiToken(), rootOrg, // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req) @@ -211,13 +204,12 @@ usersApi.get('/:slug/watched', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const slug = req.params.slug - const userUid = await getUserUID(req, userId) + const userUid = await getUserUIDBySession(req) const url = API_ENDPOINTS.getUsersWatchedTopics(slug) + `?_uid=${userUid}` const response = await axios.get( url, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, - nodebb_authorization_token: getWriteApiToken(), rootOrg, // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req) @@ -252,7 +244,7 @@ usersApi.get('/:slug/about', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const slug = req.params.slug - const userUid = await getUserUID(req, userId) + const userUid = await getUserUIDBySession(req) logInfo('called /:slug/about slug=> ', slug) const url = API_ENDPOINTS.getUserProfile(slug) + `?_uid=${userUid}` logInfo('called /:slug/about url=> ', url) @@ -260,7 +252,6 @@ usersApi.get('/:slug/about', async (req, res) => { url, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, - nodebb_authorization_token: getWriteApiToken(), // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req) } } diff --git a/src/protectedApi_v8/discussionHub/writeApi.ts b/src/protectedApi_v8/discussionHub/writeApi.ts index 6054275e..32e07048 100644 --- a/src/protectedApi_v8/discussionHub/writeApi.ts +++ b/src/protectedApi_v8/discussionHub/writeApi.ts @@ -2,7 +2,7 @@ import axios from 'axios' import { Router } from 'express' import { getRootOrg } from '../../authoring/utils/header' import { axiosRequestConfig } from '../../configs/request.config' -import { getUserUID, getWriteApiAdminUID, getWriteApiToken } from '../../utils/discussionHub-helper' +import { getUserUIDBySession, getWriteApiAdminUID, getWriteApiToken } from '../../utils/discussionHub-helper' import { CONSTANTS } from '../../utils/env' import { logError, logInfo } from '../../utils/logger' import { extractUserIdFromRequest, extractUserToken } from '../../utils/requestExtract' @@ -38,7 +38,6 @@ export async function createDiscussionHubUser(req: any , user: any): Promise { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const url = API_ENDPOINTS.createTopic - const userUid = await getUserUID(req, userId) + const userUid = await getUserUIDBySession(req) const response = await axios.post( url, { @@ -70,7 +69,6 @@ writeApi.post('/topics', async (req, res) => { }, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, - nodebb_authorization_token: getWriteApiToken(), // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req) } } @@ -92,7 +90,7 @@ writeApi.post('/topics/:topicId', async (req, res) => { logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const topicId = req.params.topicId const url = API_ENDPOINTS.replyToTopic(topicId) - const userUid = await getUserUID(req, userId) + const userUid = await getUserUIDBySession(req) const response = await axios.post( url, { @@ -101,7 +99,6 @@ writeApi.post('/topics/:topicId', async (req, res) => { }, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, - nodebb_authorization_token: getWriteApiToken(), // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req) } } @@ -137,7 +134,7 @@ writeApi.post('/posts/:postId/bookmark', async (req, res) => { logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const postId = req.params.postId const url = API_ENDPOINTS.bookmarkPost(postId) - const userUid = await getUserUID(req, userId) + const userUid = await getUserUIDBySession(req) const response = await axios.post( url, { @@ -145,7 +142,6 @@ writeApi.post('/posts/:postId/bookmark', async (req, res) => { }, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, - nodebb_authorization_token: getWriteApiToken(), // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req) } } @@ -166,13 +162,12 @@ writeApi.delete('/posts/:postId/bookmark', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const postId = req.params.postId - const userUid = await getUserUID(req, userId) + const userUid = await getUserUIDBySession(req) const url = API_ENDPOINTS.bookmarkPost(postId) + `?_uid=${userUid}` const response = await axios.delete( url, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, - nodebb_authorization_token: getWriteApiToken(), // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req) } } @@ -194,7 +189,7 @@ writeApi.post('/posts/:postId/vote', async (req, res) => { logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const postId = req.params.postId const url = API_ENDPOINTS.votePost(postId) - const userUid = await getUserUID(req, userId) + const userUid = await getUserUIDBySession(req) const response = await axios.post( url, { @@ -203,7 +198,6 @@ writeApi.post('/posts/:postId/vote', async (req, res) => { }, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, - nodebb_authorization_token: getWriteApiToken(), // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req) } } @@ -224,13 +218,12 @@ writeApi.delete('/posts/:postId/vote', async (req, res) => { const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const postId = req.params.postId - const userUid = await getUserUID(req, userId) + const userUid = await getUserUIDBySession(req) const url = API_ENDPOINTS.votePost(postId) + `?_uid=${userUid}` const response = await axios.delete( url, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, - nodebb_authorization_token: getWriteApiToken(), // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req) } } @@ -252,7 +245,7 @@ writeApi.put('/topics/:topicId/follow', async (req, res) => { logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) const topicId = req.params.topicId const url = API_ENDPOINTS.followTopic(topicId) - const userUid = await getUserUID(req, userId) + const userUid = await getUserUIDBySession(req) const response = await axios.put( url, { @@ -261,7 +254,6 @@ writeApi.put('/topics/:topicId/follow', async (req, res) => { }, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, - nodebb_authorization_token: getWriteApiToken(), // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req) } } @@ -290,7 +282,6 @@ writeApi.put('/topics/:topicId/tags', async (req, res) => { }, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, - nodebb_authorization_token: getWriteApiToken(), // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req) } } diff --git a/src/utils/discussionHub-helper.ts b/src/utils/discussionHub-helper.ts index 2bef841c..a723c7c8 100644 --- a/src/utils/discussionHub-helper.ts +++ b/src/utils/discussionHub-helper.ts @@ -21,7 +21,7 @@ export function getWriteApiAdminUID(): number { } // tslint:disable-next-line: no-any -export async function getUserUID(req: any, wid: any) { +export async function getUserUID1(req: any, wid: any) { // tslint:disable-next-line: no-any const userPresent = await getUserByUsername(req, wid).catch(async (_err: any) => { // If user is not already present in nodeBB NodeBB DiscussionHub @@ -34,6 +34,12 @@ export async function getUserUID(req: any, wid: any) { } } +// tslint:disable-next-line: no-any +export async function getUserUIDBySession(req: any) { + // tslint:disable-next-line: no-any + return req.session.uid +} + // tslint:disable-next-line: no-any export async function getUserSlug(req: any, wid: any) { // tslint:disable-next-line: no-any From a99127ad8f209d85b61ed0c6c4fd4eb540aa296c Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Wed, 25 Aug 2021 14:59:30 +0530 Subject: [PATCH 135/856] Import issue --- src/protectedApi_v8/discussionHub/notifications.ts | 2 +- src/protectedApi_v8/discussionHub/topics.ts | 2 +- src/protectedApi_v8/discussionHub/users.ts | 2 +- src/protectedApi_v8/discussionHub/writeApi.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/protectedApi_v8/discussionHub/notifications.ts b/src/protectedApi_v8/discussionHub/notifications.ts index bc8ca5ba..ebcbdb3c 100644 --- a/src/protectedApi_v8/discussionHub/notifications.ts +++ b/src/protectedApi_v8/discussionHub/notifications.ts @@ -2,7 +2,7 @@ import axios from 'axios' import { Router } from 'express' import { getRootOrg } from '../../authoring/utils/header' import { axiosRequestConfig } from '../../configs/request.config' -import { getUserUIDBySession, getWriteApiToken } from '../../utils/discussionHub-helper' +import { getUserUIDBySession} from '../../utils/discussionHub-helper' import { CONSTANTS } from '../../utils/env' import { logError, logInfo } from '../../utils/logger' import { extractUserIdFromRequest , extractUserToken} from '../../utils/requestExtract' diff --git a/src/protectedApi_v8/discussionHub/topics.ts b/src/protectedApi_v8/discussionHub/topics.ts index 2f6b3962..a85a404b 100644 --- a/src/protectedApi_v8/discussionHub/topics.ts +++ b/src/protectedApi_v8/discussionHub/topics.ts @@ -2,7 +2,7 @@ import axios from 'axios' import { Router } from 'express' import { getRootOrg } from '../../authoring/utils/header' import { axiosRequestConfig } from '../../configs/request.config' -import { getUserUIDBySession, getWriteApiToken } from '../../utils/discussionHub-helper' +import { getUserUIDBySession} from '../../utils/discussionHub-helper' import { CONSTANTS } from '../../utils/env' import { logError, logInfo } from '../../utils/logger' import { extractUserIdFromRequest, extractUserToken } from '../../utils/requestExtract' diff --git a/src/protectedApi_v8/discussionHub/users.ts b/src/protectedApi_v8/discussionHub/users.ts index 0497bea6..b0cb758c 100644 --- a/src/protectedApi_v8/discussionHub/users.ts +++ b/src/protectedApi_v8/discussionHub/users.ts @@ -2,7 +2,7 @@ import axios from 'axios' import { Router } from 'express' import { getRootOrg } from '../../authoring/utils/header' import { axiosRequestConfig } from '../../configs/request.config' -import { getUserSlug, getUserUIDBySession, getWriteApiToken } from '../../utils/discussionHub-helper' +import { getUserSlug, getUserUIDBySession} from '../../utils/discussionHub-helper' import { CONSTANTS } from '../../utils/env' import { logError, logInfo } from '../../utils/logger' import { extractUserIdFromRequest, extractUserToken} from '../../utils/requestExtract' diff --git a/src/protectedApi_v8/discussionHub/writeApi.ts b/src/protectedApi_v8/discussionHub/writeApi.ts index 32e07048..0757e141 100644 --- a/src/protectedApi_v8/discussionHub/writeApi.ts +++ b/src/protectedApi_v8/discussionHub/writeApi.ts @@ -2,7 +2,7 @@ import axios from 'axios' import { Router } from 'express' import { getRootOrg } from '../../authoring/utils/header' import { axiosRequestConfig } from '../../configs/request.config' -import { getUserUIDBySession, getWriteApiAdminUID, getWriteApiToken } from '../../utils/discussionHub-helper' +import { getUserUIDBySession, getWriteApiAdminUID} from '../../utils/discussionHub-helper' import { CONSTANTS } from '../../utils/env' import { logError, logInfo } from '../../utils/logger' import { extractUserIdFromRequest, extractUserToken } from '../../utils/requestExtract' From 8c0f9853085a48bd8e24d8670244870fb68f4aee Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Mon, 30 Aug 2021 13:21:35 +0530 Subject: [PATCH 136/856] Changes for public apis --- src/protectedApi_v8/discussionHub/topics.ts | 4 ++-- src/utils/apiWhiteList.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/protectedApi_v8/discussionHub/topics.ts b/src/protectedApi_v8/discussionHub/topics.ts index 731ee7a7..7be7c218 100644 --- a/src/protectedApi_v8/discussionHub/topics.ts +++ b/src/protectedApi_v8/discussionHub/topics.ts @@ -98,7 +98,7 @@ topicsApi.get('/unread', async (req, res) => { const rootOrg = getRootOrg(req) const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) - const userUid = await getUserUID(req,userId) + const userUid = await getUserUID(req, userId) const url = API_ENDPOINTS.getUnreadTopics + `?_uid=${userUid}` const response = await axios.get( url, @@ -124,7 +124,7 @@ topicsApi.get('/unread/total', async (req, res) => { const rootOrg = getRootOrg(req) const userId = extractUserIdFromRequest(req) logInfo(`UserId: ${userId}, rootOrg: ${rootOrg}`) - const userUid = await getUserUID(req,userId) + const userUid = await getUserUID(req, userId) const url = API_ENDPOINTS.getUnreadTopicsTotal + `?_uid=${userUid}` const response = await axios.get( url, diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index 172cfeb1..f709bba1 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -33,6 +33,7 @@ const checkIsStaticRoute = (REQ_URL: any) => { '/assets/', '/content-plugins/', '/editors/', + '/public/', ] // tslint:disable-next-line: no-any return _.some(excludePath, (path: any) => _.includes(REQ_URL, path)) From 381f2295f173dd33caa11c331f7409f8a775d73a Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Mon, 30 Aug 2021 15:22:28 +0530 Subject: [PATCH 137/856] Updated API to use proper Backend APIs --- src/protectedApi_v8/user/profile-details.ts | 63 +++++++++++++++------ 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/src/protectedApi_v8/user/profile-details.ts b/src/protectedApi_v8/user/profile-details.ts index de197a87..4168083d 100644 --- a/src/protectedApi_v8/user/profile-details.ts +++ b/src/protectedApi_v8/user/profile-details.ts @@ -578,11 +578,32 @@ profileDeatailsApi.post('/createUserWithoutInvitationEmail', async (req, res) => const searchresponse = await axios({ ...axiosRequestConfig, data: { request: { query: '', filters: { email: sbemail_.toLowerCase() } } }, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), + }, method: 'POST', - url: API_END_POINTS.searchSb, + url: API_END_POINTS.kongSearchUser, }) if (searchresponse.data.result.response.count > 0) { - res.status(400).send(emailAdressExist) + res.status(400).send({ + id: 'api.error.createUser', + ver: '1.0', + // tslint:disable-next-line: object-literal-sort-keys + ts: dateFormat(new Date(), 'yyyy-mm-dd HH:MM:ss:lo'), + params: + { + resmsgid: uuidv1(), + // tslint:disable-next-line: object-literal-sort-keys + msgid: null, + status: 'failed', + err: 'USR_EMAIL_EXISTS', + errmsg: emailAdressExist, + }, + responseCode: 'USR_EMAIL_EXISTS', + result: {}, + }) return } else { const sbUserProfile: Partial = { @@ -598,7 +619,7 @@ profileDeatailsApi.post('/createUserWithoutInvitationEmail', async (req, res) => 'x-authenticated-user-token': extractUserToken(req), }, method: 'POST', - url: API_END_POINTS.createSb, + url: API_END_POINTS.kongCreateUser, }) if (response.data.responseCode === 'CLIENT_ERROR') { res.status(400).send(failedToCreateUser) @@ -613,31 +634,39 @@ profileDeatailsApi.post('/createUserWithoutInvitationEmail', async (req, res) => 'x-authenticated-user-token': extractUserToken(req), }, method: 'GET', - url: API_END_POINTS.userRead(sbUserId), + url: API_END_POINTS.kongUserRead(sbUserId), }) if (sbUserReadResponse.data.params.status !== 'success') { res.status(500).send(failedToReadUser) return } - const personalDetailsRegistry: IPersonalDetails = { - firstname: sbfirstName_, - primaryEmail: sbemail_, - surname: sblastName_, - userName: sbUserReadResponse.data.result.response.userName, + const sbProfileUpdateReq = { + profileDetails: { + employmentDetails: { + departmentName: sbChannel, + }, + personalDetails: { + firstname: sbfirstName_, + primaryEmail: sbemail_, + surname: sblastName_, + }, + }, + userId: sbUserId, } - const userRegistry = getUserRegistry(personalDetailsRegistry, sbChannel) - const userRegistryResponse = await axios({ + + const sbUserProfileUpdateResp = await axios({ ...axiosRequestConfig, - data: userRegistry, + data: { request: sbProfileUpdateReq }, headers: { - wid: sbUserId, + Authorization: CONSTANTS.SB_API_KEY, }, - method: 'POST', - url: API_END_POINTS.createOSUserRegistry(sbUserId), + method: 'PATCH', + url: API_END_POINTS.kongUpdateUser, }) - if (userRegistryResponse.data === null) { - res.status(500).send(failedToCreateUserInOpenSaber) + if (sbUserProfileUpdateResp.data.responseCode === 'CLIENT_ERROR') { + res.status(400).send(failedToUpdateUser) + return } else { const sbUserProfileResponse: Partial = { email: sbemail_, firstName: sbfirstName_, lastName: sblastName_, From f43a6a98d85a9c6c68f1f37bb58612afc64efc9a Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Mon, 30 Aug 2021 15:33:31 +0530 Subject: [PATCH 138/856] Added createUserWithoutInvitationEmail API in whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 4423b5f8..88d1f525 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1188,6 +1188,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/user/profileDetails/createUserWithoutInvitationEmail': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -1355,5 +1362,6 @@ export const API_LIST = { '/protected/v8/connections/v2/add/connection', '/protected/v8/connections/v2/connections/suggests', '/protected/v8/connections/v2/update/connection', + '/protected/v8/user/profileDetails/createUserWithoutInvitationEmail', ], } From 6bcc11729c51632e11a5649575c3e36afbb1f0dc Mon Sep 17 00:00:00 2001 From: Amit Kumar Yadav <36166686+amit-kumaryadav@users.noreply.github.com> Date: Mon, 30 Aug 2021 16:10:52 +0530 Subject: [PATCH 139/856] Removed getUserUID1 method --- src/utils/discussionHub-helper.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/utils/discussionHub-helper.ts b/src/utils/discussionHub-helper.ts index a723c7c8..fef4ed5c 100644 --- a/src/utils/discussionHub-helper.ts +++ b/src/utils/discussionHub-helper.ts @@ -20,20 +20,6 @@ export function getWriteApiAdminUID(): number { } } -// tslint:disable-next-line: no-any -export async function getUserUID1(req: any, wid: any) { - // tslint:disable-next-line: no-any - const userPresent = await getUserByUsername(req, wid).catch(async (_err: any) => { - // If user is not already present in nodeBB NodeBB DiscussionHub - logError(usrNotFound) - return Promise.reject(new Error(usrNotFound)) - }) - if (userPresent && userPresent.uid) { - logInfo('user found - uid: ', userPresent.uid) - return Promise.resolve(userPresent.uid) - } -} - // tslint:disable-next-line: no-any export async function getUserUIDBySession(req: any) { // tslint:disable-next-line: no-any From 3b0282f6ea248226a1bb133b117b626a97c605e0 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Thu, 2 Sep 2021 15:08:02 +0530 Subject: [PATCH 140/856] Changes for frac api --- src/protectedApi_v8/frac.ts | 19 +++++++++++++++++++ src/utils/env.ts | 2 +- src/utils/whitelistApis.ts | 8 ++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/protectedApi_v8/frac.ts b/src/protectedApi_v8/frac.ts index ccdb1826..a48eeb9b 100644 --- a/src/protectedApi_v8/frac.ts +++ b/src/protectedApi_v8/frac.ts @@ -10,6 +10,7 @@ import { extractAuthorizationFromRequest } from '../utils/requestExtract' const API_END_POINTS = { addDataNode: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/addDataNode`, addDataNodeBulk: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/addDataNodeBulk`, + filterByMappings: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/filterByMappings`, getActivity: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/getAllNodes?type=ACTIVITY&status=VERIFIED`, getCompetencyArea: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/getAllNodes?type=COMPETENCYAREA`, getDictionary: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/getAllNodes?type=COMPETENCY&status=VERIFIED`, @@ -113,6 +114,24 @@ fracApi.post('/searchNodes', async (req, res) => { } }) +fracApi.post('/filterByMappings', async (req, res) => { + try { + const response = await axios.post(API_END_POINTS.filterByMappings, req.body, { + ...axiosRequestConfig, + headers: { + Authorization: extractAuthorizationFromRequest(req), + }, + }) + res.status(response.status).send(response.data) + } catch (err) { + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: unknownError, + } + ) + } +}) + fracApi.get('/getNodeById/:id/:type', async (req, res) => { try { const id = req.params.id diff --git a/src/utils/env.ts b/src/utils/env.ts index c4f22b3b..2a2d4781 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -127,7 +127,7 @@ export const CONSTANTS = { COHORTS_API_BASE: env.COHORTS_API_BASE || env.SB_EXT_API_BASE_2, CONTENT_SOURCE_API_BASE: env.CONTENT_SOURCE_API_BASE || env.SB_EXT_API_BASE_2, CONTINUE_LEARNING_API_BASE: env.CONTINUE_LEARNING_API_BASE || env.SB_EXT_API_BASE_2, - FRAC_API_BASE: env.FRAC_API_BASE || 'https://igot-frac-dev.tarento.com', + FRAC_API_BASE: env.FRAC_API_BASE || 'https://frac.igot-dev.in', NETWORK_SERVICE_BACKEND: env.NETWOR_SERVICE_API_BASE || 'http:localhost:7001', CONTENT_VALIDATION_API_BASE: env.CONTENT_VALIDATION_API_BASE || 'http://localhost:6590', PROFANITY_SERVICE_API_BASE: env.PROFANITY_SERVICE_API_BASE || 'http://localhost:4001', diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 88d1f525..a499fe30 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -747,6 +747,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/frac/filterByMappings': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/frac/:type/:key': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1299,6 +1306,7 @@ export const API_LIST = { '/protected/v8/workallocation/getWorkOrderById/:workOrderId', '/protected/v8/workallocation/user/autocomplete/:searchTerm', '/protected/v8/frac/searchNodes', + '/protected/v8/frac/filterByMappings', '/protected/v8/frac/:type/:key', '/protected/v8/workallocation/v2/add', '/protected/v8/workallocation/getWorkAllocationById/:workAllocationId', From 7babc18eb4355598fe755f47e1b6d89f06a1943b Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Fri, 3 Sep 2021 15:57:18 +0530 Subject: [PATCH 141/856] Added new designations --- src/static-data/designation.json | 40 +++++++++++++++++++------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/src/static-data/designation.json b/src/static-data/designation.json index 4863e422..d0a24adf 100644 --- a/src/static-data/designation.json +++ b/src/static-data/designation.json @@ -1,27 +1,35 @@ { "designations": [ - "Secretary", + "AD(OL)", "Additional Secretary", - "Joint Secretary", + "Assistant Director (English Shorthand and Typ", + "Assistant Director Office Management", + "Assistant Section Officer (ASO)", + "DD(Cost)", + "DD(OL)", + "Deputy Secretary (iGOT-ISTM)", + "Deputy Director (Peripatetic Training)", "Director", + "Director (Coordination)", + "Director( Admin and GA)", + "Director (External assistance and internation", + "Director Vigilance", "DS", - "PSO", - "Sr. PPS", - "Under Secretary", - "DD(Cost)", - "PPS", "JD(OL)", - "DD(OL)", - "AD(OL)", - "SO", - "PS", - "Assistant Section Officer (ASO)", + "Joint Secretary", + "Junior Secretariat Assistant (JSA)", + "Junior Translation Officer (JTO)", "Personal Assistant (PA)", + "PS", + "PSO", + "PPS", + "Secretary", + "Senior Secretariat Assistant (SSA)", "Senior Translation Officer (STO)", - "Junior Translation Officer (JTO)", + "SO", + "Sr. PPS", "Stenographer Grade 'C'", - "Senior Secretariat Assistant (SSA)", - "Junior Secretariat Assistant (JSA)" + "Under Secretary" ], "gradePay": [ "1", @@ -54,4 +62,4 @@ "28", "29" ] -} \ No newline at end of file +} From 447a9130c0d015b56ba7d90593c9dd2c7c86b373 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Fri, 3 Sep 2021 17:45:48 +0530 Subject: [PATCH 142/856] Added content search api to white list --- src/proxies_v8/proxies_v8.ts | 5 +++++ src/utils/whitelistApis.ts | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index c15150da..f06f56f2 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -157,6 +157,11 @@ proxiesV8.use( proxyCreatorRoute(express.Router(), CONSTANTS.WEB_HOST_PROXY + '/web-hosted') ) +proxiesV8.use('/contentsearch/*', + // tslint:disable-next-line: max-line-length + proxyCreatorSunbirdSearch(express.Router(), `${CONSTANTS.KONG_API_BASE}/content/v1/search`) +) + proxiesV8.use('/sunbirdigot/*', // tslint:disable-next-line: max-line-length proxyCreatorSunbirdSearch(express.Router(), `${CONSTANTS.SUNBIRD_PROXY_API_BASE}/composite/v1/search`) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index a499fe30..5376810c 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -128,6 +128,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/contentsearch/search': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/read/content-progres/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1214,6 +1221,7 @@ export const API_LIST = { '/proxies/v8/event/v4/publish/:do_id', '/proxies/v8/event/v4/create', '/proxies/v8/user/v1/read/:id', + '/proxies/v8/contentsearch/search', '/proxies/v8/sunbirdigot/read', '/proxies/v8/sunbirdigot/search', '/proxies/v8/discussion/user/v1/create', From c8cc869e241f133d96bae45356d6ae9404a64118 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Tue, 7 Sep 2021 07:00:44 +0530 Subject: [PATCH 143/856] Using frac API to get designations --- src/protectedApi_v8/user/profile-registry.ts | 47 +++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/protectedApi_v8/user/profile-registry.ts b/src/protectedApi_v8/user/profile-registry.ts index d93730ff..0a3bbf01 100644 --- a/src/protectedApi_v8/user/profile-registry.ts +++ b/src/protectedApi_v8/user/profile-registry.ts @@ -3,11 +3,14 @@ import { Router } from 'express' import { axiosRequestConfig, axiosRequestConfigLong } from '../../configs/request.config' import { CONSTANTS } from '../../utils/env' import { logError, logInfo } from '../../utils/logger' -import { extractUserIdFromRequest } from '../../utils/requestExtract' +import { extractAuthorizationFromRequest, + extractUserIdFromRequest, + IAuthorizedRequest } from '../../utils/requestExtract' const fs = require('fs') const API_END_POINTS = { createUserRegistry: (userId: string) => `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/create/profile?userId=${userId}`, + getAllPosition: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/getAllNodes?type=POSITION&status=VERIFIED`, getUserRegistry: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/get/profile`, getUserRegistryById: (userId: string) => `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/search/profile?userId=${userId}`, searchUserRegistry: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/search/profile`, @@ -192,10 +195,15 @@ profileRegistryApi.get('/getProfilePageMeta', async (_req, res) => { .catch((err) => { logError(`error fetching degreesMeta`, err) }) - const designations = await designationMeta() + let designations = Object.create({}) + designations = await designationMeta() .catch((err) => { logError(`error fetching designationMeta`, err) }) + designations.designations = await designationMetaFrac(_req) + .catch((err) => { + logError('error fetching desingations from FRAC', err) + }) res.json({ degrees, designations, @@ -370,3 +378,38 @@ export async function getProfileStatus(userId: string) { return false } } + +export interface IPosition { + type: string + id: string + name: string + description: string + status: string + secondaryStatus: string + source: string + active: string +} + +export async function designationMetaFrac(req: IAuthorizedRequest) { + return new Promise(async (resolve, reject) => { + try { + const response = await axios.get(API_END_POINTS.getAllPosition, { + ...axiosRequestConfig, + headers: { + Authorization: extractAuthorizationFromRequest(req), + }, + }) + if (response.data.responseData) { + const result = response.data.responseData.map((item: IPosition) => { + return { name : item.name } + }) + resolve(result) + } else { + reject('Failed to receive response from FRAC API for designations') + } + } catch (err) { + logError('ERROR while calling FRAC API to get Designation') + throw err + } + }) +} From e73e7ea1caa47ce4cedd4aa24f52ce187a2ae53a Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 9 Sep 2021 17:16:03 +0530 Subject: [PATCH 144/856] Added API mappings for TrainingPlatform feature --- src/authoring/content/index.ts | 150 +++++++++++++++++++++++++++++++-- 1 file changed, 144 insertions(+), 6 deletions(-) diff --git a/src/authoring/content/index.ts b/src/authoring/content/index.ts index bf4ad3b0..e7b60d59 100644 --- a/src/authoring/content/index.ts +++ b/src/authoring/content/index.ts @@ -1,8 +1,10 @@ import axios from 'axios' import { Request, Response, Router } from 'express' +import { axiosRequestConfig } from '../../configs/request.config' import { AxiosRequestConfig } from '../../models/axios-request-config.model' import { logError } from '../../utils/logger' -import { ERROR } from '../constants/error' +import { ERROR } from '../../utils/message' +import { extractUserToken } from '../../utils/requestExtract' import { IUploadS3Request, IUploadS3Response } from '../models/response/custom-s3-upload' import { decoder } from '../utils/decode' import { getOrg, getRootOrg } from '../utils/header' @@ -17,6 +19,19 @@ import { getHierarchyV2WithContent, getMultipleHierarchyV2WithContent } from './ import { searchForOtherLanguage } from './language-search' export const authApi = Router() +const failedToProcess = 'Failed to process the request. ' + +const API_END_POINTS = { + addCertToCourseBatch: `${CONSTANTS.KONG_API_BASE}/course/batch/cert/v1/template/add`, + batchAddUser: (batchId: string) => `${CONSTANTS.KONG_API_BASE}/course/v1/batch/user/add/${batchId}`, + batchRemoveUser: `${CONSTANTS.KONG_API_BASE}/course/v1/unenrol`, + createBatch: `${CONSTANTS.KONG_API_BASE}/course/v1/batch/create`, + downloadCert: `${CONSTANTS.KONG_API_BASE}/certreg/v1/certs/download`, + issueCertToCourseBatch: `${CONSTANTS.KONG_API_BASE}/course/batch/cert/v1/issue?reIssue=true`, + readCert: (certId: string) => `${CONSTANTS.KONG_API_BASE}/certreg/v2/certs/download/${certId}`, + removeCertFromCourseBatch: `${CONSTANTS.KONG_API_BASE}/course/batch/cert/v1/template/remove`, + updateBatch: `${CONSTANTS.KONG_API_BASE}/course/v1/batch/update`, +} authApi.all('*', (req, _res, next) => { if (req.body && req.body.data && typeof req.body.data === 'string') { @@ -34,7 +49,7 @@ authApi.get('/hierarchy/:id', async (req: Request, res: Response) => { } catch (ex) { logError(ex) res.status(400).send({ - msg: ERROR.GENERAL, + msg: ERROR.GENERAL_ERR_MSG, }) } }) @@ -48,7 +63,7 @@ authApi.get('/hierarchy/content/:id', async (req: Request, res: Response) => { } catch (ex) { logError(ex) res.status(400).send({ - msg: ERROR.GENERAL, + msg: ERROR.GENERAL_ERR_MSG, }) } }) @@ -62,7 +77,7 @@ authApi.get('/hierarchy/multiple/:ids', async (req: Request, res: Response) => { } catch (ex) { logError(ex) res.status(400).send({ - msg: ERROR.GENERAL, + msg: ERROR.GENERAL_ERR_MSG, }) } }) @@ -77,7 +92,7 @@ authApi.get('/hierarchy/multiple/content/:ids', async (req: Request, res: Respon } catch (ex) { logError(ex) res.status(400).send({ - msg: ERROR.GENERAL, + msg: ERROR.GENERAL_ERR_MSG, }) } }) @@ -97,7 +112,7 @@ authApi.get('/hierarchy/content/translation/:id', async (req, res) => { } catch (ex) { logError(ex) res.status(400).send({ - msg: ERROR.GENERAL, + msg: ERROR.GENERAL_ERR_MSG, }) } }) @@ -211,6 +226,129 @@ authApi.patch('/content/v3/update/:id', async (req: Request, res: Response) => { }) }) +authApi.post('/batch/:key', async (req: Request, res: Response) => { + try { + const key = req.params.key as string + let targetUrl = '' + switch (key) { + case 'create': + targetUrl = API_END_POINTS.createBatch + break + case 'removeUser': + targetUrl = API_END_POINTS.batchRemoveUser + break + case 'issueBatchCert': + targetUrl = API_END_POINTS.issueCertToCourseBatch + break + case 'downloadCert': + targetUrl = API_END_POINTS.downloadCert + break + default: + logError('Unsupported operation received for batch API - ' + key) + res.status(400).send('Unsupported operation name - ' + key) + break + } + const response = await axios.post(targetUrl, req.body, { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), + }, + }) + res.status(response.status).send(response.data) + } catch (err) { + logError(failedToProcess + err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: ERROR.GENERAL_ERR_MSG, + } + ) + } +}) + +authApi.patch('/batch/:key', async (req: Request, res: Response) => { + try { + const key = req.params.key as string + let targetUrl = '' + switch (key) { + case 'update': + targetUrl = API_END_POINTS.updateBatch + break + case 'addCert': + targetUrl = API_END_POINTS.addCertToCourseBatch + break + case 'removeCert': + targetUrl = API_END_POINTS.removeCertFromCourseBatch + break + default: + logError('Unsupported operation received for batch API - ' + key) + res.status(400).send('Unsupported operation name - ' + key) + break + } + const response = await axios.patch(targetUrl, req.body, { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), + }, + }) + res.status(response.status).send(response.data) + } catch (err) { + logError(failedToProcess + err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: ERROR.GENERAL_ERR_MSG, + } + ) + } +}) + +authApi.post('/addUserToBatch/:batchId', async (req: Request, res: Response) => { + try { + const batchId = req.params.batchId as string + const response = await axios.post(API_END_POINTS.batchAddUser(batchId), req.body, { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), + }, + }) + res.status(response.status).send(response.data) + } catch (err) { + logError(failedToProcess + err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: ERROR.GENERAL_ERR_MSG, + } + ) + } +}) + +authApi.get('/readCert/:certId', async (req, res) => { + try { + const certId = req.params.certId + const response = await axios.get(API_END_POINTS.readCert(certId), { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), + }, + }) + res.status(response.status).send(response.data) + } catch (err) { + logError(failedToProcess + err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: ERROR.GENERAL_ERR_MSG, + } + ) + } +}) + authApi.all('*', (req: Request, res: Response) => { axios({ data: req.body, From ccad23692e465e68fa1339873dc420bae16d0347 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Thu, 16 Sep 2021 14:41:34 +0530 Subject: [PATCH 145/856] Added get user competencies from WAT data --- src/protectedApi_v8/workallocation.ts | 30 +++++++++++++++++++++++++++ src/utils/whitelistApis.ts | 8 +++++++ 2 files changed, 38 insertions(+) diff --git a/src/protectedApi_v8/workallocation.ts b/src/protectedApi_v8/workallocation.ts index 2262e663..198974e7 100644 --- a/src/protectedApi_v8/workallocation.ts +++ b/src/protectedApi_v8/workallocation.ts @@ -15,6 +15,7 @@ const API_END_POINTS = { copyWorkOrderEndPoint: (path: string) => `${CONSTANTS.KONG_API_BASE}/${path}/copy/workOrder`, getPdf: (id: string) => `${CONSTANTS.KONG_API_BASE}/getWOPdf/${id}`, getUserBasicDetails: (userId: string) => `${CONSTANTS.KONG_API_BASE}/${workallocationV2Path}/user/basicInfo/${userId}`, + getUserCompetenciesDetails: (userId: string) => `${CONSTANTS.KONG_API_BASE}/${workallocationV2Path}/user/competencies/${userId}`, getUsersEndPoint: `${CONSTANTS.SB_EXT_API_BASE_2}/v1/workallocation/getUsers`, getWorkAllocationById: (path: string, id: string) => `${CONSTANTS.KONG_API_BASE}/${path}/getWorkAllocationById/${id}`, getWorkOrderById: (path: string, id: string) => `${CONSTANTS.KONG_API_BASE}/${path}/getWorkOrderById/${id}`, @@ -435,3 +436,32 @@ workAllocationApi.get('/getWOPdf/:workOrderId', async (req, res) => { ) } }) + +workAllocationApi.get('/getUserCompetencies/:userId', async (req, res) => { + try { + const userId = req.params.userId + if (!userId) { + res.status(400).send(userIdFailedMessage) + return + } + const response = await axios.get( + API_END_POINTS.getUserCompetenciesDetails(userId), + { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), + }, + } + ) + res.status(response.status).send(response.data) + } catch (err) { + logError(Error + err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: ERROR.GENERAL_ERR_MSG, + } + ) + } +}) \ No newline at end of file diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 5376810c..1ca30966 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -747,6 +747,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/workallocation/getUserCompetencies/:userId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/frac/searchNodes': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1313,6 +1320,7 @@ export const API_LIST = { '/protected/v8/workallocation/add/workorder', '/protected/v8/workallocation/getWorkOrderById/:workOrderId', '/protected/v8/workallocation/user/autocomplete/:searchTerm', + '/protected/v8/workallocation/getUserCompetencies/:userId', '/protected/v8/frac/searchNodes', '/protected/v8/frac/filterByMappings', '/protected/v8/frac/:type/:key', From 1ed8abdbf6dc21af1c3c391cd5136008e7f88fa1 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Thu, 16 Sep 2021 19:47:03 +0530 Subject: [PATCH 146/856] Added TrainingPlatform APIs to Whitelist --- src/utils/whitelistApis.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 5376810c..29b5a5d5 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1209,12 +1209,36 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/authApi/batch/:key': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/authApi/addUserToBatch/:batchId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/authApi/readCert/:certId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ '/authApi/content/v3/create', '/authApi/content/v3/read/:do_id', '/authApi/content/v3/update/:do_id', + '/authApi/batch/:key', + '/authApi/addUserToBatch/:batchId', + '/authApi/readCert/:certId', '/proxies/v8/api/user/v2/read', '/proxies/v8/api/user/v2/read/:id', '/proxies/v8/event/v4/read/:do_id', From e213ab1332aa920afcd27c8d2ff0d41b4a089ece Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Fri, 17 Sep 2021 06:21:36 +0530 Subject: [PATCH 147/856] Updated Training Platform APIs --- src/authoring/content/index.ts | 27 ++++----------------------- src/utils/whitelistApis.ts | 11 +---------- 2 files changed, 5 insertions(+), 33 deletions(-) diff --git a/src/authoring/content/index.ts b/src/authoring/content/index.ts index e7b60d59..22256239 100644 --- a/src/authoring/content/index.ts +++ b/src/authoring/content/index.ts @@ -23,7 +23,7 @@ const failedToProcess = 'Failed to process the request. ' const API_END_POINTS = { addCertToCourseBatch: `${CONSTANTS.KONG_API_BASE}/course/batch/cert/v1/template/add`, - batchAddUser: (batchId: string) => `${CONSTANTS.KONG_API_BASE}/course/v1/batch/user/add/${batchId}`, + batchAddUser: `${CONSTANTS.KONG_API_BASE}/course/v1/enrol`, batchRemoveUser: `${CONSTANTS.KONG_API_BASE}/course/v1/unenrol`, createBatch: `${CONSTANTS.KONG_API_BASE}/course/v1/batch/create`, downloadCert: `${CONSTANTS.KONG_API_BASE}/certreg/v1/certs/download`, @@ -234,6 +234,9 @@ authApi.post('/batch/:key', async (req: Request, res: Response) => { case 'create': targetUrl = API_END_POINTS.createBatch break + case 'addUser': + targetUrl = API_END_POINTS.batchAddUser + break case 'removeUser': targetUrl = API_END_POINTS.batchRemoveUser break @@ -305,28 +308,6 @@ authApi.patch('/batch/:key', async (req: Request, res: Response) => { } }) -authApi.post('/addUserToBatch/:batchId', async (req: Request, res: Response) => { - try { - const batchId = req.params.batchId as string - const response = await axios.post(API_END_POINTS.batchAddUser(batchId), req.body, { - ...axiosRequestConfig, - headers: { - Authorization: CONSTANTS.SB_API_KEY, - // tslint:disable-next-line: no-duplicate-string - 'x-authenticated-user-token': extractUserToken(req), - }, - }) - res.status(response.status).send(response.data) - } catch (err) { - logError(failedToProcess + err) - res.status((err && err.response && err.response.status) || 500).send( - (err && err.response && err.response.data) || { - error: ERROR.GENERAL_ERR_MSG, - } - ) - } -}) - authApi.get('/readCert/:certId', async (req, res) => { try { const certId = req.params.certId diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 29b5a5d5..b17e6576 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1215,15 +1215,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.PUBLIC, ], - }, - '/authApi/addUserToBatch/:batchId': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, - '/authApi/readCert/:certId': { + },'/authApi/readCert/:certId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -1237,7 +1229,6 @@ export const API_LIST = { '/authApi/content/v3/read/:do_id', '/authApi/content/v3/update/:do_id', '/authApi/batch/:key', - '/authApi/addUserToBatch/:batchId', '/authApi/readCert/:certId', '/proxies/v8/api/user/v2/read', '/proxies/v8/api/user/v2/read/:id', From 5bfd118351c8ea5288b1ecfb67315603a126c184 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Mon, 20 Sep 2021 15:34:27 +0530 Subject: [PATCH 148/856] UI-Proxy routing clean-up --- src/authoring/content/index.ts | 24 ++++++++++++++++++------ src/proxies_v8/proxies_v8.ts | 6 +++--- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/authoring/content/index.ts b/src/authoring/content/index.ts index 22256239..b730f8f4 100644 --- a/src/authoring/content/index.ts +++ b/src/authoring/content/index.ts @@ -185,9 +185,13 @@ authApi.post('/download/s3', async (req: Request, res: Response) => { authApi.post('/content/v3/create', async (request: Request, res: Response) => { axios({ data: request.body, - headers: request.headers, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(request), + }, method: request.method, - url: CONSTANTS.SUNBIRD_PROXY_URL + request.url, + url: CONSTANTS.KONG_API_BASE + request.url, } as AxiosRequestConfig) .then((response) => { res.status(response.status).send(response.data) @@ -199,9 +203,13 @@ authApi.post('/content/v3/create', async (request: Request, res: Response) => { authApi.get('/content/v3/read/:id', async (req: Request, res: Response) => { axios({ - headers: req.headers, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), + }, method: req.method, - url: CONSTANTS.SUNBIRD_PROXY_URL + req.url, + url: CONSTANTS.KONG_API_BASE + req.url, } as AxiosRequestConfig) .then((response) => { res.status(response.status).send(response.data) @@ -214,9 +222,13 @@ authApi.get('/content/v3/read/:id', async (req: Request, res: Response) => { authApi.patch('/content/v3/update/:id', async (req: Request, res: Response) => { axios({ data: req.body, - headers: req.headers, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), + }, method: req.method, - url: CONSTANTS.SUNBIRD_PROXY_URL + req.url, + url: CONSTANTS.KONG_API_BASE + req.url, } as AxiosRequestConfig) .then((response) => { res.status(response.status).send(response.data) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index f06f56f2..aa501d6c 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -164,7 +164,7 @@ proxiesV8.use('/contentsearch/*', proxiesV8.use('/sunbirdigot/*', // tslint:disable-next-line: max-line-length - proxyCreatorSunbirdSearch(express.Router(), `${CONSTANTS.SUNBIRD_PROXY_API_BASE}/composite/v1/search`) + proxyCreatorSunbirdSearch(express.Router(), `${CONSTANTS.KONG_API_BASE}/composite/v1/search`) ) proxiesV8.use('/v1/content/retire', @@ -181,11 +181,11 @@ proxiesV8.use('/learnervm/private/content/*', proxiesV8.use('/content-progres/*', // tslint:disable-next-line: max-line-length - proxyCreatorSunbirdSearch(express.Router(), `${CONSTANTS.SUNBIRD_PROXY_API_BASE}/course/v1/content/state/update`) + proxyCreatorSunbirdSearch(express.Router(), `${CONSTANTS.KONG_API_BASE}/course/v1/content/state/update`) ) proxiesV8.use('/read/content-progres/*', // tslint:disable-next-line: max-line-length - proxyCreatorSunbirdSearch(express.Router(), `${CONSTANTS.SUNBIRD_PROXY_API_BASE}/course/v1/content/state/read`) + proxyCreatorSunbirdSearch(express.Router(), `${CONSTANTS.KONG_API_BASE}/course/v1/content/state/read`) ) proxiesV8.use('/api/user/v2/read', From 18f38fc1cc09982df7f539d5e74078f18bf37e72 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Mon, 20 Sep 2021 17:37:59 +0530 Subject: [PATCH 149/856] routes issue --- src/authoring/content/index.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/authoring/content/index.ts b/src/authoring/content/index.ts index b730f8f4..1962613a 100644 --- a/src/authoring/content/index.ts +++ b/src/authoring/content/index.ts @@ -2,7 +2,7 @@ import axios from 'axios' import { Request, Response, Router } from 'express' import { axiosRequestConfig } from '../../configs/request.config' import { AxiosRequestConfig } from '../../models/axios-request-config.model' -import { logError } from '../../utils/logger' +import { logError, logInfo } from '../../utils/logger' import { ERROR } from '../../utils/message' import { extractUserToken } from '../../utils/requestExtract' import { IUploadS3Request, IUploadS3Response } from '../models/response/custom-s3-upload' @@ -183,6 +183,7 @@ authApi.post('/download/s3', async (req: Request, res: Response) => { }) authApi.post('/content/v3/create', async (request: Request, res: Response) => { + logInfo('Request URL ' + request.url) axios({ data: request.body, headers: { @@ -191,7 +192,7 @@ authApi.post('/content/v3/create', async (request: Request, res: Response) => { 'x-authenticated-user-token': extractUserToken(request), }, method: request.method, - url: CONSTANTS.KONG_API_BASE + request.url, + url: CONSTANTS.KNOWLEDGE_MW_API_BASE + request.url, } as AxiosRequestConfig) .then((response) => { res.status(response.status).send(response.data) @@ -209,7 +210,7 @@ authApi.get('/content/v3/read/:id', async (req: Request, res: Response) => { 'x-authenticated-user-token': extractUserToken(req), }, method: req.method, - url: CONSTANTS.KONG_API_BASE + req.url, + url: CONSTANTS.KNOWLEDGE_MW_API_BASE + req.url, } as AxiosRequestConfig) .then((response) => { res.status(response.status).send(response.data) @@ -228,7 +229,7 @@ authApi.patch('/content/v3/update/:id', async (req: Request, res: Response) => { 'x-authenticated-user-token': extractUserToken(req), }, method: req.method, - url: CONSTANTS.KONG_API_BASE + req.url, + url: CONSTANTS.KNOWLEDGE_MW_API_BASE + req.url, } as AxiosRequestConfig) .then((response) => { res.status(response.status).send(response.data) From 577e18848b2607562adc59a5a2493f235b3f3841 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Mon, 20 Sep 2021 17:58:38 +0530 Subject: [PATCH 150/856] routes issue --- src/authoring/content/index.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/authoring/content/index.ts b/src/authoring/content/index.ts index 1962613a..2bfddbd8 100644 --- a/src/authoring/content/index.ts +++ b/src/authoring/content/index.ts @@ -2,7 +2,7 @@ import axios from 'axios' import { Request, Response, Router } from 'express' import { axiosRequestConfig } from '../../configs/request.config' import { AxiosRequestConfig } from '../../models/axios-request-config.model' -import { logError, logInfo } from '../../utils/logger' +import { logError} from '../../utils/logger' import { ERROR } from '../../utils/message' import { extractUserToken } from '../../utils/requestExtract' import { IUploadS3Request, IUploadS3Response } from '../models/response/custom-s3-upload' @@ -20,6 +20,7 @@ import { searchForOtherLanguage } from './language-search' export const authApi = Router() const failedToProcess = 'Failed to process the request. ' +const actionConst = '/action' const API_END_POINTS = { addCertToCourseBatch: `${CONSTANTS.KONG_API_BASE}/course/batch/cert/v1/template/add`, @@ -183,7 +184,6 @@ authApi.post('/download/s3', async (req: Request, res: Response) => { }) authApi.post('/content/v3/create', async (request: Request, res: Response) => { - logInfo('Request URL ' + request.url) axios({ data: request.body, headers: { @@ -192,7 +192,7 @@ authApi.post('/content/v3/create', async (request: Request, res: Response) => { 'x-authenticated-user-token': extractUserToken(request), }, method: request.method, - url: CONSTANTS.KNOWLEDGE_MW_API_BASE + request.url, + url: CONSTANTS.KNOWLEDGE_MW_API_BASE + actionConst + request.url, } as AxiosRequestConfig) .then((response) => { res.status(response.status).send(response.data) @@ -210,7 +210,7 @@ authApi.get('/content/v3/read/:id', async (req: Request, res: Response) => { 'x-authenticated-user-token': extractUserToken(req), }, method: req.method, - url: CONSTANTS.KNOWLEDGE_MW_API_BASE + req.url, + url: CONSTANTS.KNOWLEDGE_MW_API_BASE + actionConst + req.url, } as AxiosRequestConfig) .then((response) => { res.status(response.status).send(response.data) @@ -229,7 +229,7 @@ authApi.patch('/content/v3/update/:id', async (req: Request, res: Response) => { 'x-authenticated-user-token': extractUserToken(req), }, method: req.method, - url: CONSTANTS.KNOWLEDGE_MW_API_BASE + req.url, + url: CONSTANTS.KNOWLEDGE_MW_API_BASE + actionConst + req.url, } as AxiosRequestConfig) .then((response) => { res.status(response.status).send(response.data) From 8a52599843a1851ad801e2d71a4a952a001c6858 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Tue, 21 Sep 2021 11:57:37 +0530 Subject: [PATCH 151/856] Added logs --- src/authoring/content/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/authoring/content/index.ts b/src/authoring/content/index.ts index 2bfddbd8..95cf7856 100644 --- a/src/authoring/content/index.ts +++ b/src/authoring/content/index.ts @@ -2,7 +2,7 @@ import axios from 'axios' import { Request, Response, Router } from 'express' import { axiosRequestConfig } from '../../configs/request.config' import { AxiosRequestConfig } from '../../models/axios-request-config.model' -import { logError} from '../../utils/logger' +import { logError, logInfo} from '../../utils/logger' import { ERROR } from '../../utils/message' import { extractUserToken } from '../../utils/requestExtract' import { IUploadS3Request, IUploadS3Response } from '../models/response/custom-s3-upload' @@ -35,8 +35,10 @@ const API_END_POINTS = { } authApi.all('*', (req, _res, next) => { + logInfo("Enter's in the all ........."); if (req.body && req.body.data && typeof req.body.data === 'string') { req.body = decoder(req.body.data) + console.log('body data=====>', req.body) } next() }) @@ -184,6 +186,7 @@ authApi.post('/download/s3', async (req: Request, res: Response) => { }) authApi.post('/content/v3/create', async (request: Request, res: Response) => { + logInfo('Requested url ============ ' + CONSTANTS.KNOWLEDGE_MW_API_BASE + actionConst + request.url) axios({ data: request.body, headers: { From e5c18ae571be78b067f16417335f9dd811f3ad1c Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Tue, 21 Sep 2021 11:59:14 +0530 Subject: [PATCH 152/856] Added logs --- src/authoring/content/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/authoring/content/index.ts b/src/authoring/content/index.ts index 95cf7856..dde5814f 100644 --- a/src/authoring/content/index.ts +++ b/src/authoring/content/index.ts @@ -35,9 +35,10 @@ const API_END_POINTS = { } authApi.all('*', (req, _res, next) => { - logInfo("Enter's in the all ........."); + logInfo("Enter's in the all .........") if (req.body && req.body.data && typeof req.body.data === 'string') { req.body = decoder(req.body.data) + // tslint:disable-next-line: no-console console.log('body data=====>', req.body) } next() From a3f7a09356f13b1020d30f540e907fd2edd9f574 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Tue, 21 Sep 2021 12:42:06 +0530 Subject: [PATCH 153/856] Headers issue --- src/authoring/content/index.ts | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/authoring/content/index.ts b/src/authoring/content/index.ts index dde5814f..00053699 100644 --- a/src/authoring/content/index.ts +++ b/src/authoring/content/index.ts @@ -4,7 +4,7 @@ import { axiosRequestConfig } from '../../configs/request.config' import { AxiosRequestConfig } from '../../models/axios-request-config.model' import { logError, logInfo} from '../../utils/logger' import { ERROR } from '../../utils/message' -import { extractUserToken } from '../../utils/requestExtract' +import { extractUserToken , extractUserIdFromRequest} from '../../utils/requestExtract' import { IUploadS3Request, IUploadS3Response } from '../models/response/custom-s3-upload' import { decoder } from '../utils/decode' import { getOrg, getRootOrg } from '../utils/header' @@ -18,6 +18,9 @@ import { getHierarchy, getMultipleHierarchyV2 } from './hierarchy' import { getHierarchyV2WithContent, getMultipleHierarchyV2WithContent } from './hierarchy-and-content' import { searchForOtherLanguage } from './language-search' +const _ = require('lodash') + + export const authApi = Router() const failedToProcess = 'Failed to process the request. ' const actionConst = '/action' @@ -35,11 +38,8 @@ const API_END_POINTS = { } authApi.all('*', (req, _res, next) => { - logInfo("Enter's in the all .........") if (req.body && req.body.data && typeof req.body.data === 'string') { req.body = decoder(req.body.data) - // tslint:disable-next-line: no-console - console.log('body data=====>', req.body) } next() }) @@ -187,15 +187,16 @@ authApi.post('/download/s3', async (req: Request, res: Response) => { }) authApi.post('/content/v3/create', async (request: Request, res: Response) => { - logInfo('Requested url ============ ' + CONSTANTS.KNOWLEDGE_MW_API_BASE + actionConst + request.url) axios({ data: request.body, headers: { Authorization: CONSTANTS.SB_API_KEY, // tslint:disable-next-line: no-duplicate-string - 'x-authenticated-user-token': extractUserToken(request), + 'x-authenticated-user-token': extractUserToken(request), + 'x-authenticated-userid' : extractUserIdFromRequest(request), + 'X-Channel-Id' : (_.get(request, 'session.rootOrgId')) ? _.get(request, 'session.rootOrgId') : CONSTANTS.X_Channel_Id }, - method: request.method, + method: request.method, url: CONSTANTS.KNOWLEDGE_MW_API_BASE + actionConst + request.url, } as AxiosRequestConfig) .then((response) => { @@ -211,7 +212,9 @@ authApi.get('/content/v3/read/:id', async (req: Request, res: Response) => { headers: { Authorization: CONSTANTS.SB_API_KEY, // tslint:disable-next-line: no-duplicate-string - 'x-authenticated-user-token': extractUserToken(req), + 'x-authenticated-user-token': extractUserToken(req), + 'x-authenticated-userid' : extractUserIdFromRequest(req), + 'X-Channel-Id' : (_.get(req, 'session.rootOrgId')) ? _.get(req, 'session.rootOrgId') : CONSTANTS.X_Channel_Id }, method: req.method, url: CONSTANTS.KNOWLEDGE_MW_API_BASE + actionConst + req.url, @@ -230,7 +233,9 @@ authApi.patch('/content/v3/update/:id', async (req: Request, res: Response) => { headers: { Authorization: CONSTANTS.SB_API_KEY, // tslint:disable-next-line: no-duplicate-string - 'x-authenticated-user-token': extractUserToken(req), + 'x-authenticated-user-token': extractUserToken(req), + 'x-authenticated-userid' : extractUserIdFromRequest(req), + 'X-Channel-Id' : (_.get(req, 'session.rootOrgId')) ? _.get(req, 'session.rootOrgId') : CONSTANTS.X_Channel_Id }, method: req.method, url: CONSTANTS.KNOWLEDGE_MW_API_BASE + actionConst + req.url, From dbc44b51b5cc75a2cee4a846b37022055ab274ff Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Tue, 21 Sep 2021 12:52:23 +0530 Subject: [PATCH 154/856] Headers issue --- src/authoring/content/index.ts | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/authoring/content/index.ts b/src/authoring/content/index.ts index 00053699..ae1469f6 100644 --- a/src/authoring/content/index.ts +++ b/src/authoring/content/index.ts @@ -2,9 +2,9 @@ import axios from 'axios' import { Request, Response, Router } from 'express' import { axiosRequestConfig } from '../../configs/request.config' import { AxiosRequestConfig } from '../../models/axios-request-config.model' -import { logError, logInfo} from '../../utils/logger' +import { logError} from '../../utils/logger' import { ERROR } from '../../utils/message' -import { extractUserToken , extractUserIdFromRequest} from '../../utils/requestExtract' +import { extractUserIdFromRequest , extractUserToken} from '../../utils/requestExtract' import { IUploadS3Request, IUploadS3Response } from '../models/response/custom-s3-upload' import { decoder } from '../utils/decode' import { getOrg, getRootOrg } from '../utils/header' @@ -20,10 +20,12 @@ import { searchForOtherLanguage } from './language-search' const _ = require('lodash') - export const authApi = Router() const failedToProcess = 'Failed to process the request. ' const actionConst = '/action' +const xAuthUserToken = 'x-authenticated-user-token' +const xAuthUserId = 'x-authenticated-userid' +const xChannelId = 'X-Channel-Id' const API_END_POINTS = { addCertToCourseBatch: `${CONSTANTS.KONG_API_BASE}/course/batch/cert/v1/template/add`, @@ -191,10 +193,9 @@ authApi.post('/content/v3/create', async (request: Request, res: Response) => { data: request.body, headers: { Authorization: CONSTANTS.SB_API_KEY, - // tslint:disable-next-line: no-duplicate-string - 'x-authenticated-user-token': extractUserToken(request), - 'x-authenticated-userid' : extractUserIdFromRequest(request), - 'X-Channel-Id' : (_.get(request, 'session.rootOrgId')) ? _.get(request, 'session.rootOrgId') : CONSTANTS.X_Channel_Id + xAuthUserToken : extractUserToken(request), + xAuthUserId : extractUserIdFromRequest(request), + xChannelId : (_.get(request, 'session.rootOrgId')) ? _.get(request, 'session.rootOrgId') : CONSTANTS.X_Channel_Id, }, method: request.method, url: CONSTANTS.KNOWLEDGE_MW_API_BASE + actionConst + request.url, @@ -211,10 +212,9 @@ authApi.get('/content/v3/read/:id', async (req: Request, res: Response) => { axios({ headers: { Authorization: CONSTANTS.SB_API_KEY, - // tslint:disable-next-line: no-duplicate-string - 'x-authenticated-user-token': extractUserToken(req), - 'x-authenticated-userid' : extractUserIdFromRequest(req), - 'X-Channel-Id' : (_.get(req, 'session.rootOrgId')) ? _.get(req, 'session.rootOrgId') : CONSTANTS.X_Channel_Id + xAuthUserToken : extractUserToken(request), + xAuthUserId : extractUserIdFromRequest(request), + xChannelId : (_.get(request, 'session.rootOrgId')) ? _.get(request, 'session.rootOrgId') : CONSTANTS.X_Channel_Id, }, method: req.method, url: CONSTANTS.KNOWLEDGE_MW_API_BASE + actionConst + req.url, @@ -232,10 +232,9 @@ authApi.patch('/content/v3/update/:id', async (req: Request, res: Response) => { data: req.body, headers: { Authorization: CONSTANTS.SB_API_KEY, - // tslint:disable-next-line: no-duplicate-string - 'x-authenticated-user-token': extractUserToken(req), - 'x-authenticated-userid' : extractUserIdFromRequest(req), - 'X-Channel-Id' : (_.get(req, 'session.rootOrgId')) ? _.get(req, 'session.rootOrgId') : CONSTANTS.X_Channel_Id + xAuthUserToken : extractUserToken(req), + xAuthUserId : extractUserIdFromRequest(req), + xChannelId : (_.get(req, 'session.rootOrgId')) ? _.get(req, 'session.rootOrgId') : CONSTANTS.X_Channel_Id, }, method: req.method, url: CONSTANTS.KNOWLEDGE_MW_API_BASE + actionConst + req.url, From 04ac9c0f2e60d8b40b812dd4cce5ae700284d269 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Tue, 21 Sep 2021 13:00:23 +0530 Subject: [PATCH 155/856] Liniting issue --- src/authoring/content/index.ts | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/authoring/content/index.ts b/src/authoring/content/index.ts index ae1469f6..cb7e0f08 100644 --- a/src/authoring/content/index.ts +++ b/src/authoring/content/index.ts @@ -23,9 +23,6 @@ const _ = require('lodash') export const authApi = Router() const failedToProcess = 'Failed to process the request. ' const actionConst = '/action' -const xAuthUserToken = 'x-authenticated-user-token' -const xAuthUserId = 'x-authenticated-userid' -const xChannelId = 'X-Channel-Id' const API_END_POINTS = { addCertToCourseBatch: `${CONSTANTS.KONG_API_BASE}/course/batch/cert/v1/template/add`, @@ -193,9 +190,12 @@ authApi.post('/content/v3/create', async (request: Request, res: Response) => { data: request.body, headers: { Authorization: CONSTANTS.SB_API_KEY, - xAuthUserToken : extractUserToken(request), - xAuthUserId : extractUserIdFromRequest(request), - xChannelId : (_.get(request, 'session.rootOrgId')) ? _.get(request, 'session.rootOrgId') : CONSTANTS.X_Channel_Id, + // tslint:disable-next-line: no-duplicate-string + 'X-Channel-Id' : (_.get(request, 'session.rootOrgId')) ? _.get(request, 'session.rootOrgId') : CONSTANTS.X_Channel_Id, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(request), + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-userid' : extractUserIdFromRequest(request), }, method: request.method, url: CONSTANTS.KNOWLEDGE_MW_API_BASE + actionConst + request.url, @@ -212,9 +212,13 @@ authApi.get('/content/v3/read/:id', async (req: Request, res: Response) => { axios({ headers: { Authorization: CONSTANTS.SB_API_KEY, - xAuthUserToken : extractUserToken(request), - xAuthUserId : extractUserIdFromRequest(request), - xChannelId : (_.get(request, 'session.rootOrgId')) ? _.get(request, 'session.rootOrgId') : CONSTANTS.X_Channel_Id, + // tslint:disable-next-line: no-duplicate-string + 'X-Channel-Id' : (_.get(req, 'session.rootOrgId')) ? _.get(req, 'session.rootOrgId') : CONSTANTS.X_Channel_Id, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-userid' : extractUserIdFromRequest(req), + }, method: req.method, url: CONSTANTS.KNOWLEDGE_MW_API_BASE + actionConst + req.url, @@ -232,9 +236,12 @@ authApi.patch('/content/v3/update/:id', async (req: Request, res: Response) => { data: req.body, headers: { Authorization: CONSTANTS.SB_API_KEY, - xAuthUserToken : extractUserToken(req), - xAuthUserId : extractUserIdFromRequest(req), - xChannelId : (_.get(req, 'session.rootOrgId')) ? _.get(req, 'session.rootOrgId') : CONSTANTS.X_Channel_Id, + // tslint:disable-next-line: no-duplicate-string + 'X-Channel-Id' : (_.get(req, 'session.rootOrgId')) ? _.get(req, 'session.rootOrgId') : CONSTANTS.X_Channel_Id, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-userid' : extractUserIdFromRequest(req), }, method: req.method, url: CONSTANTS.KNOWLEDGE_MW_API_BASE + actionConst + req.url, From e48cd30bf6cd68de8969a41658a70ccba0b191e5 Mon Sep 17 00:00:00 2001 From: darshannagesh Date: Tue, 28 Sep 2021 13:25:52 +0530 Subject: [PATCH 156/856] Adding Dashboard Paths --- src/proxies_v8/proxies_v8.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index aa501d6c..14d1d20b 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -223,6 +223,10 @@ proxiesV8.use('/org/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) +proxiesV8.use('/dashboard/*', + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + proxiesV8.use('/user/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) From 75d8cbbc5772a63b06ef789c32d7d0537c63364f Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Wed, 29 Sep 2021 17:59:22 +0530 Subject: [PATCH 157/856] Added searchBy path --- src/proxies_v8/proxies_v8.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 14d1d20b..c1a37bd9 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -235,6 +235,10 @@ proxiesV8.use('/event/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) +proxiesV8.use('/searchBy/*', + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + // proxiesV8.use('/api/framework/*', // // tslint:disable-next-line: max-line-length // proxyCreatorQML(express.Router(), `${CONSTANTS.KONG_API_BASE}`, '/api/') From c09f8bfba3cc7d9370c637a0efee6d055600514e Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Wed, 29 Sep 2021 18:01:09 +0530 Subject: [PATCH 158/856] Added searchBy API in whitelist --- src/utils/whitelistApis.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index d3599e0a..8ec5e628 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1228,6 +1228,12 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.PUBLIC, ], + },'/searchBy/:key': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], }, }, URL_PATTERN: @@ -1402,5 +1408,6 @@ export const API_LIST = { '/protected/v8/connections/v2/connections/suggests', '/protected/v8/connections/v2/update/connection', '/protected/v8/user/profileDetails/createUserWithoutInvitationEmail', + '/searchBy/:key', ], } From 029764b8a352df4dd5fc04da34b461c835f31fd6 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Wed, 29 Sep 2021 18:17:36 +0530 Subject: [PATCH 159/856] Updated URL Path --- src/utils/whitelistApis.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 8ec5e628..a1f28684 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1228,7 +1228,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.PUBLIC, ], - },'/searchBy/:key': { + },'/proxies/v8/searchBy/:key': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -1408,6 +1408,6 @@ export const API_LIST = { '/protected/v8/connections/v2/connections/suggests', '/protected/v8/connections/v2/update/connection', '/protected/v8/user/profileDetails/createUserWithoutInvitationEmail', - '/searchBy/:key', + '/proxies/v8/searchBy/:key', ], } From e74fc13b4443c9e6fe556513dd90e8176128c021 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Mon, 4 Oct 2021 11:10:20 +0530 Subject: [PATCH 160/856] Changes for PM Dashboard apis --- src/protectedApi_v8/workallocation.ts | 2 +- src/proxies_v8/proxies_v8.ts | 10 ++++++++ src/utils/env.ts | 2 ++ src/utils/proxyCreator.ts | 9 ++++++- src/utils/whitelistApis.ts | 35 ++++++++++++++++++++++++++- 5 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/protectedApi_v8/workallocation.ts b/src/protectedApi_v8/workallocation.ts index 198974e7..98847691 100644 --- a/src/protectedApi_v8/workallocation.ts +++ b/src/protectedApi_v8/workallocation.ts @@ -464,4 +464,4 @@ workAllocationApi.get('/getUserCompetencies/:userId', async (req, res) => { } ) } -}) \ No newline at end of file +}) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index aa501d6c..ab172fad 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -241,6 +241,16 @@ proxiesV8.use('/api/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) +proxiesV8.use('/dashboard/*', + // tslint:disable-next-line: max-line-length + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + +proxiesV8.use('/wat/dashboard/*', + // tslint:disable-next-line: max-line-length + proxyCreatorSunbird(express.Router(), `${CONSTANTS.DASHBOARD_API_BASE}`) +) + proxiesV8.use('/data/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) diff --git a/src/utils/env.ts b/src/utils/env.ts index 2a2d4781..d3b5a3fb 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -22,6 +22,7 @@ export const CONSTANTS = { CONTENT_STORE_DEVELOPMENT_BASE: '', CORS_ENVIRONMENT: env.CORS_ENVIRONMENT || 'prod', COUNTER: 'http://localhost:5903', + DASHBOARD_API_BASE: env.DASHBOARD_API_BASE || 'https://igot-dashboard.tarento.com/api', DEFAULT_ORG: env.DEFAULT_ORG || 'dopt', DEFAULT_ROOT_ORG: env.DEFAULT_ROOT_ORG || 'igot', ES_BASE: env.ES_BASE || 'http://localhost:9200', @@ -88,6 +89,7 @@ export const CONSTANTS = { PORTAL_PORT: parseInt(env.PORTAL_PORT + '', 10) || 3003, PREFERENCE_API_BASE: env.PREFERENCE_API_BASE || env.SB_EXT_API_BASE_4, PROGRESS_API_BASE: env.PROGRESS_API_BASE || env.SB_EXT_API_BASE_2, + PM_DASHBOARD_API_BASE: env.PM_DASHBOARD_API_BASE || 'https://pm.igot-dev.in', RATING_API_BASE: env.RATING_API_BASE || env.SB_EXT_API_BASE_2 || 'http://localhost:7001', RECOMMENDATION_API_BASE: env.RECOMMENDATION_API_BASE || env.SBEXT_API_BASE, RESET_PASSWORD: '', diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 2c900a83..a46c0544 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -11,6 +11,7 @@ const proxyCreator = (timeout = 10000) => createProxyServer({ }) const proxy = createProxyServer({}) const PROXY_SLUG = '/proxies/v8' +const PROXY_SLUG_WAT = '/proxies/v8/wat' // tslint:disable-next-line: no-any proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { @@ -140,7 +141,13 @@ export function proxyCreatorSunbird(route: Router, targetUrl: string, _timeout = route.all('/*', (req, res) => { // tslint:disable-next-line: no-console console.log('REQ_URL_ORIGINAL proxyCreatorSunbird', req.originalUrl) - let url = removePrefix(`${PROXY_SLUG}`, req.originalUrl) + let url = '' + if (req.originalUrl.includes('/proxies/v8/wat')) { + url = removePrefix(`${PROXY_SLUG_WAT}`, req.originalUrl) + } else { + url = removePrefix(`${PROXY_SLUG}`, req.originalUrl) + } + if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create') && req.session) { if (req.originalUrl.includes('?')) { url = `${url}&_uid=${req.session.uid}` diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index d3599e0a..dd0e0495 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -261,6 +261,34 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/dashboard/analytics/getDashboardConfig/Karmayogi/:comp': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/dashboard/analytics/getDashboardsForProfile/Karmayogi': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/dashboard/analytics/getChartV2/Karmayogi': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/wat/dashboard/getDashboardConfig/Karmayogi/overview': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/upload/action/content/v3/upload/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1222,7 +1250,8 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.PUBLIC, ], - },'/authApi/readCert/:certId': { + }, + '/authApi/readCert/:certId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -1301,6 +1330,10 @@ export const API_LIST = { '/proxies/v8/user/private/v1/assign/role/userrole', '/proxies/v8/learnervm/private/content/v3/retire/', '/proxies/v8/private/content/v3/update/:do_id', + '/proxies/v8/dashboard/analytics/getDashboardConfig/Karmayogi/:comp', + '/proxies/v8/dashboard/analytics/getDashboardsForProfile/Karmayogi', + '/proxies/v8/dashboard/analytics/getChartV2/Karmayogi', + '/proxies/v8/wat/dashboard/getDashboardConfig/Karmayogi/overview', '/protected/v8/user/profileDetails/test', '/protected/v8/resource/', '/protected/v8/user/details', From 273570899fca125f513b8c899ea019743ea7084a Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Wed, 6 Oct 2021 16:19:06 +0530 Subject: [PATCH 161/856] Added new API mappings for MDO --- src/proxies_v8/proxies_v8.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 94e42328..f2ffcd94 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -239,6 +239,18 @@ proxiesV8.use('/searchBy/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) +proxiesV8.use('/staff/*', + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + +proxiesV8.use('/budget/*', + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + +proxiesV8.use('/orghistory/*', + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + // proxiesV8.use('/api/framework/*', // // tslint:disable-next-line: max-line-length // proxyCreatorQML(express.Router(), `${CONSTANTS.KONG_API_BASE}`, '/api/') From 8cab5f953cf84c0cf81934fad1813bcc46c213f9 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Wed, 6 Oct 2021 16:25:33 +0530 Subject: [PATCH 162/856] Added MDO APIs in whitelist --- src/utils/whitelistApis.ts | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index d281cf69..e33215fc 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1263,6 +1263,36 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.PUBLIC, ], + },'/proxies/v8/staff/position': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + },'/proxies/v8/staff/position/:orgId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + },'/proxies/v8/budget/scheme': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + },'/proxies/v8/budget/scheme/:orgId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + },'/proxies/v8/orghistory/:orgId/:key': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], }, }, URL_PATTERN: @@ -1442,5 +1472,10 @@ export const API_LIST = { '/protected/v8/connections/v2/update/connection', '/protected/v8/user/profileDetails/createUserWithoutInvitationEmail', '/proxies/v8/searchBy/:key', + '/proxies/v8/staff/position', + '/proxies/v8/staff/position/:orgId', + '/proxies/v8/budget/scheme', + '/proxies/v8/budget/scheme/:orgId', + '/proxies/v8/orghistory/:orgId/:key' ], } From 4803a673c35ec4dd4b0a499931edea4594aa8033 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Wed, 27 Oct 2021 16:47:47 +0530 Subject: [PATCH 163/856] Changes for role check refs. #55911 --- src/publicApi_v8/publicApiV8.ts | 4 - src/server.ts | 4 +- src/utils/apiWhiteList.ts | 70 ++++++++++- src/utils/roles.ts | 19 +++ src/utils/whitelistApis.ts | 204 +++++++++++++++----------------- 5 files changed, 186 insertions(+), 115 deletions(-) create mode 100644 src/utils/roles.ts diff --git a/src/publicApi_v8/publicApiV8.ts b/src/publicApi_v8/publicApiV8.ts index dbbc8e61..597d6b0c 100644 --- a/src/publicApi_v8/publicApiV8.ts +++ b/src/publicApi_v8/publicApiV8.ts @@ -1,8 +1,6 @@ import express from 'express' import { CONSTANTS } from '../utils/env' import { proxyCreatorRoute } from '../utils/proxyCreator' -import { signup } from './signup' -import { publicTnc } from './tnc' import { workallocationPublic } from './workallocationPublic' export const publicApiV8 = express.Router() @@ -16,6 +14,4 @@ publicApiV8.get('/', (_req, res) => { publicApiV8.use('/assets', proxyCreatorRoute(express.Router(), CONSTANTS.WEB_HOST_PROXY + '/web-hosted/web-client-public-assets')) -publicApiV8.use('/tnc', publicTnc) -publicApiV8.use('/signup', signup) publicApiV8.use('/workallocation', workallocationPublic) diff --git a/src/server.ts b/src/server.ts index 8940218f..380a7e7b 100644 --- a/src/server.ts +++ b/src/server.ts @@ -46,6 +46,8 @@ export class Server { } const sessionConfig = getSessionConfig() this.app.use(expressSession(sessionConfig)) + this.app.use(express.urlencoded({ extended: false, limit: '50mb' })) + this.app.use(express.json({ limit: '50mb' })) this.app.all('*', apiWhiteListLogger()) if (CONSTANTS.PORTAL_API_WHITELIST_CHECK === 'true') { this.app.all('*', isAllowed()) @@ -82,8 +84,6 @@ export class Server { private configureMiddleware() { this.app.use(connectTimeout('240s')) this.app.use(compression()) - this.app.use(express.urlencoded({ extended: false, limit: '50mb' })) - this.app.use(express.json({ limit: '50mb' })) this.app.use(fileUpload()) // this.app.use(cors()) this.app.use('/healthcheck', healthcheck({ diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index f709bba1..73f0c4ce 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -6,6 +6,7 @@ const dateFormat = require('dateformat') import { NextFunction, Request, Response } from 'express' import { CONSTANTS } from './env' import { logError, logInfo } from './logger' +import { ROLE } from './roles' import { API_LIST } from './whitelistApis' /** @@ -50,9 +51,39 @@ const shouldAllow = (req: Request) => { * @since release-3.1.0 */ const urlChecks = { + // tslint:disable-next-line: no-any + OWNER_CHECK: async (resolve: any, reject: any, req: Request, checksParams: any, REQ_URL: any) => { + if (_.get(checksParams, 'checks')) { + // tslint:disable-next-line: no-any + const ownerChecks: any[] = [] + // tslint:disable-next-line: no-any + checksParams.checks.forEach((ownerCheckObj: any) => { + ownerChecks.push(new Promise((res, rej) => { + const _checkFor = _.get(ownerCheckObj, 'entity') + if (_checkFor && typeof urlChecks[_checkFor] === 'function') { + urlChecks[_checkFor](res, rej, req, ownerCheckObj, REQ_URL) + } + })) + }) + try { + await Promise.all(ownerChecks) + .then((_pSuccess) => { + resolve() + }) + .catch((pError) => { + return reject(pError) + }) + } catch (error) { + return reject() + } + } else { + return reject('Owner check validation failed.') + } + }, // tslint:disable-next-line: no-any ROLE_CHECK: (resolve: any, reject: any, req: Request, rolesForURL: any, REQ_URL: any) => { - const data = (_.get(req, 'session.userRoles')) ? _.get(req, 'session.userRoles') : [] + const roleData = _.get(req, 'session.userRoles') + const data = (roleData) ? roleData : [] logInfo('Portal_API_WHITELIST : Middleware for URL [ ' + REQ_URL + ' ]') if (_.includes(rolesForURL, 'ALL') && data.length > 0) { resolve() @@ -91,6 +122,43 @@ const urlChecks = { return reject('User doesn\'t have appropriate roles') } }, + // Callback to `OWNER_CHECK` promise object + // tslint:disable-next-line: no-any + __adminCheck__userId: (resolve: any, reject: any, req: any, ownerCheckObj: any, _REQ_URL: any) => { + try { + const _sessionUserId = _.get(req, 'session.userId') + const _reqUserId = _.get(ownerCheckObj, 'params') ? _.get(req, ownerCheckObj.params) : _.get(req, 'body.userId') + const _sessionRole = _.get(req, 'session.userRoles') + if (_.includes(_sessionRole, ROLE.MDO_ADMIN)) { + resolve() + } else { + if (_sessionUserId === _reqUserId) { + resolve() + } else { + return reject('Mismatch in user id verification. Session UserId [ ' + _sessionUserId + + ' ] is not an admin or does not match with request body UserId [ ' + _reqUserId + ' ]') + } + } + } catch (error) { + return reject('User id validation failed.') + } + }, + // Callback to `OWNER_CHECK` promise object + // tslint:disable-next-line: no-any + __session__userId: (resolve: any, reject: any, req: any, ownerCheckObj: any, _REQ_URL: any) => { + try { + const _sessionUserId = _.get(req, 'session.userId') + const _reqUserId = _.get(ownerCheckObj, 'params') ? _.get(req, ownerCheckObj.params) : _.get(req, 'body.request.userId') + if (_sessionUserId === _reqUserId) { + resolve() + } else { + return reject('Mismatch in user id verification. Session UserId [ ' + _sessionUserId + + ' ] does not match with request body UserId [ ' + _reqUserId + ' ]') + } + } catch (error) { + return reject('User id validation failed.') + } + }, } /** diff --git a/src/utils/roles.ts b/src/utils/roles.ts new file mode 100644 index 00000000..97fb8f97 --- /dev/null +++ b/src/utils/roles.ts @@ -0,0 +1,19 @@ +// Valid Roles +export const ROLE = { + CBC_ADMIN: 'CBC_ADMIN', + CBC_MEMBER: 'CBC_MEMBER', + CONTENT_CREATOR: 'CONTENT_CREATOR', + CONTENT_PUBLISHER: 'CONTENT_PUBLISHER', + CONTENT_REVIEWER: 'CONTENT_REVIEWER', + EDITOR: 'EDITOR', + FRAC_ACCESS_COMPENTENCY: 'FRAC_COMPETENCY_MEMBER', + FRAC_ADMIN: 'FRAC_ADMIN', + FRAC_COMPETENCY_REVIEWER: 'FRAC_COMPETENCY_REVIEWER', + FRAC_REVIEWER_ONE: 'FRAC_REVIEWER_L1', + FRAC_REVIEWER_TWO: 'FRAC_REVIEWER_L2', + IFU_MEMBER: 'IFU_MEMBER', + MDO_ADMIN: 'MDO_ADMIN', + PUBLIC: 'PUBLIC', + SPV_ADMIN: 'SPV_ADMIN', + WAT_MEMBER: 'WAT_MEMBER', +} diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index e33215fc..7860b6c6 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1,54 +1,37 @@ 'use strict' +import { ROLE } from './roles' + const CHECK = { + OWNER_CHECK: 'OWNER_CHECK', ROLE: 'ROLE_CHECK', SCOPE: 'SCOPE_CHECK', } -// Valid Roles -const ROLE = { - CBC_ADMIN: 'CBC_ADMIN', - CBC_MEMBER: 'CBC_MEMBER', - CONTENT_CREATOR: 'CONTENT_CREATOR', - CONTENT_PUBLISHER: 'CONTENT_PUBLISHER', - CONTENT_REVIEWER: 'CONTENT_REVIEWER', - EDITOR: 'EDITOR', - FRAC_ACCESS_COMPENTENCY: 'FRAC_COMPETENCY_MEMBER', - FRAC_ADMIN: 'FRAC_ADMIN', - FRAC_COMPETENCY_REVIEWER: 'FRAC_COMPETENCY_REVIEWER', - FRAC_REVIEWER_ONE: 'FRAC_REVIEWER_L1', - FRAC_REVIEWER_TWO: 'FRAC_REVIEWER_L2', - IFU_MEMBER: 'IFU_MEMBER', - MDO_ADMIN: 'MDO_ADMIN', - PUBLIC: 'PUBLIC', - SPV_ADMIN: 'SPV_ADMIN', - WAT_MEMBER: 'WAT_MEMBER', -} - // All api list validations export const API_LIST = { URL: { - '/authApi/content/v3/create': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, - '/authApi/content/v3/read/:do_id': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, - '/authApi/content/v3/update/:do_id': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, + // '/authApi/content/v3/create': { + // checksNeeded: [CHECK.ROLE], + // // tslint:disable-next-line: object-literal-sort-keys + // ROLE_CHECK: [ + // ROLE.PUBLIC, + // ], + // }, + // '/authApi/content/v3/read/:do_id': { + // checksNeeded: [CHECK.ROLE], + // // tslint:disable-next-line: object-literal-sort-keys + // ROLE_CHECK: [ + // ROLE.PUBLIC, + // ], + // }, + // '/authApi/content/v3/update/:do_id': { + // checksNeeded: [CHECK.ROLE], + // // tslint:disable-next-line: object-literal-sort-keys + // ROLE_CHECK: [ + // ROLE.PUBLIC, + // ], + // }, '/protected/v8/user/details': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -922,55 +905,55 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/protected/v8/connections/connections/recommended': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, - '/protected/v8/connections/connections/requests/received': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, - '/protected/v8/connections/connections/established': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, - '/protected/v8/connections/connections/established/:id': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, - '/protected/v8/connections/connections/requested': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, - '/protected/v8/connections/add/connection': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, - '/protected/v8/connections/connections/suggests': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, + // '/protected/v8/connections/connections/recommended': { + // checksNeeded: [CHECK.ROLE], + // // tslint:disable-next-line: object-literal-sort-keys + // ROLE_CHECK: [ + // ROLE.PUBLIC, + // ], + // }, + // '/protected/v8/connections/connections/requests/received': { + // checksNeeded: [CHECK.ROLE], + // // tslint:disable-next-line: object-literal-sort-keys + // ROLE_CHECK: [ + // ROLE.PUBLIC, + // ], + // }, + // '/protected/v8/connections/connections/established': { + // checksNeeded: [CHECK.ROLE], + // // tslint:disable-next-line: object-literal-sort-keys + // ROLE_CHECK: [ + // ROLE.PUBLIC, + // ], + // }, + // '/protected/v8/connections/connections/established/:id': { + // checksNeeded: [CHECK.ROLE], + // // tslint:disable-next-line: object-literal-sort-keys + // ROLE_CHECK: [ + // ROLE.PUBLIC, + // ], + // }, + // '/protected/v8/connections/connections/requested': { + // checksNeeded: [CHECK.ROLE], + // // tslint:disable-next-line: object-literal-sort-keys + // ROLE_CHECK: [ + // ROLE.PUBLIC, + // ], + // }, + // '/protected/v8/connections/add/connection': { + // checksNeeded: [CHECK.ROLE], + // // tslint:disable-next-line: object-literal-sort-keys + // ROLE_CHECK: [ + // ROLE.PUBLIC, + // ], + // }, + // '/protected/v8/connections/connections/suggests': { + // checksNeeded: [CHECK.ROLE], + // // tslint:disable-next-line: object-literal-sort-keys + // ROLE_CHECK: [ + // ROLE.PUBLIC, + // ], + // }, '/protected/v8/cohorts/user/autoenrollment/:courseId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1217,11 +1200,16 @@ export const API_LIST = { ], }, '/protected/v8/connections/v2/add/connection': { - checksNeeded: [CHECK.ROLE], + checksNeeded: [CHECK.ROLE, CHECK.OWNER_CHECK], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], + OWNER_CHECK: { + checks: [ + { entity: '__session__userId', params: 'body.userIdFrom' }, + ], + }, }, '/protected/v8/connections/v2/connections/suggests': { checksNeeded: [CHECK.ROLE], @@ -1257,37 +1245,37 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.PUBLIC, ], - },'/proxies/v8/searchBy/:key': { + }, '/proxies/v8/searchBy/:key': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], - },'/proxies/v8/staff/position': { + }, '/proxies/v8/staff/position': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], - },'/proxies/v8/staff/position/:orgId': { + }, '/proxies/v8/staff/position/:orgId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], - },'/proxies/v8/budget/scheme': { + }, '/proxies/v8/budget/scheme': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], - },'/proxies/v8/budget/scheme/:orgId': { + }, '/proxies/v8/budget/scheme/:orgId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], - },'/proxies/v8/orghistory/:orgId/:key': { + }, '/proxies/v8/orghistory/:orgId/:key': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -1297,9 +1285,9 @@ export const API_LIST = { }, URL_PATTERN: [ - '/authApi/content/v3/create', - '/authApi/content/v3/read/:do_id', - '/authApi/content/v3/update/:do_id', + // '/authApi/content/v3/create', + // '/authApi/content/v3/read/:do_id', + // '/authApi/content/v3/update/:do_id', '/authApi/batch/:key', '/authApi/readCert/:certId', '/proxies/v8/api/user/v2/read', @@ -1426,13 +1414,13 @@ export const API_LIST = { '/protected/v8/portal/cbc/department/:deptId/', '/protected/v8/portal/spv/department/:deptId/', '/protected/v8/scroing/calculate', - '/protected/v8/connections/connections/recommended', - '/protected/v8/connections/connections/requests/received', - '/protected/v8/connections/connections/established', - '/protected/v8/connections/connections/established/:id', - '/protected/v8/connections/connections/requested', - '/protected/v8/connections/add/connection', - '/protected/v8/connections/connections/suggests', + // '/protected/v8/connections/connections/recommended', + // '/protected/v8/connections/connections/requests/received', + // '/protected/v8/connections/connections/established', + // '/protected/v8/connections/connections/established/:id', + // '/protected/v8/connections/connections/requested', + // '/protected/v8/connections/add/connection', + // '/protected/v8/connections/connections/suggests', '/protected/v8/cohorts/user/autoenrollment/:courseId', '/protected/v8/profanity/startPdfProfanity', '/protected/v8/profanity/getPdfProfanityForContent/:contentId', @@ -1476,6 +1464,6 @@ export const API_LIST = { '/proxies/v8/staff/position/:orgId', '/proxies/v8/budget/scheme', '/proxies/v8/budget/scheme/:orgId', - '/proxies/v8/orghistory/:orgId/:key' + '/proxies/v8/orghistory/:orgId/:key', ], } From 74720f8f89b7130310a1c0d2bf3f5ac787dfce77 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Wed, 27 Oct 2021 17:38:16 +0530 Subject: [PATCH 164/856] Changes for role check refs. #55911 --- src/utils/apiWhiteList.ts | 17 ++++++++--------- src/utils/whitelistApis.ts | 8 ++++---- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index 73f0c4ce..c911832c 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -52,7 +52,7 @@ const shouldAllow = (req: Request) => { */ const urlChecks = { // tslint:disable-next-line: no-any - OWNER_CHECK: async (resolve: any, reject: any, req: Request, checksParams: any, REQ_URL: any) => { + PARAM_EQUALITY_CHECK: async (resolve: any, reject: any, req: Request, checksParams: any, REQ_URL: any) => { if (_.get(checksParams, 'checks')) { // tslint:disable-next-line: no-any const ownerChecks: any[] = [] @@ -122,7 +122,7 @@ const urlChecks = { return reject('User doesn\'t have appropriate roles') } }, - // Callback to `OWNER_CHECK` promise object + // Callback to `PARAM_EQUALITY_CHECK` promise object // tslint:disable-next-line: no-any __adminCheck__userId: (resolve: any, reject: any, req: any, ownerCheckObj: any, _REQ_URL: any) => { try { @@ -143,17 +143,16 @@ const urlChecks = { return reject('User id validation failed.') } }, - // Callback to `OWNER_CHECK` promise object + // Callback to `PARAM_EQUALITY_CHECK` promise object // tslint:disable-next-line: no-any - __session__userId: (resolve: any, reject: any, req: any, ownerCheckObj: any, _REQ_URL: any) => { + __param__equality: (resolve: any, reject: any, req: any, ownerCheckObj: any, _REQ_URL: any) => { try { - const _sessionUserId = _.get(req, 'session.userId') - const _reqUserId = _.get(ownerCheckObj, 'params') ? _.get(req, ownerCheckObj.params) : _.get(req, 'body.request.userId') - if (_sessionUserId === _reqUserId) { + const comparedWith = _.get(ownerCheckObj, 'session') ? _.get(req, ownerCheckObj.session) : null + const comparedFrom = _.get(ownerCheckObj, 'requestbody') ? _.get(req, ownerCheckObj.requestbody) : null + if (comparedWith === comparedFrom) { resolve() } else { - return reject('Mismatch in user id verification. Session UserId [ ' + _sessionUserId + - ' ] does not match with request body UserId [ ' + _reqUserId + ' ]') + return reject('Mismatch in param validation') } } catch (error) { return reject('User id validation failed.') diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 7860b6c6..b070cdda 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2,7 +2,7 @@ import { ROLE } from './roles' const CHECK = { - OWNER_CHECK: 'OWNER_CHECK', + PARAM_EQUALITY_CHECK: 'PARAM_EQUALITY_CHECK', ROLE: 'ROLE_CHECK', SCOPE: 'SCOPE_CHECK', } @@ -1200,14 +1200,14 @@ export const API_LIST = { ], }, '/protected/v8/connections/v2/add/connection': { - checksNeeded: [CHECK.ROLE, CHECK.OWNER_CHECK], + checksNeeded: [CHECK.ROLE, CHECK.PARAM_EQUALITY_CHECK], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], - OWNER_CHECK: { + PARAM_EQUALITY_CHECK: { checks: [ - { entity: '__session__userId', params: 'body.userIdFrom' }, + { entity: '__param__equality', session: 'session.userId', requestbody: 'body.userIdFrom' }, ], }, }, From 0a19f17163ca6485a6f0434acea3e706a0041e17 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Tue, 2 Nov 2021 11:17:58 +0530 Subject: [PATCH 165/856] Changes for api security . refs #55911 --- src/utils/apiWhiteList.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index c911832c..3e02eeab 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -149,7 +149,14 @@ const urlChecks = { try { const comparedWith = _.get(ownerCheckObj, 'session') ? _.get(req, ownerCheckObj.session) : null const comparedFrom = _.get(ownerCheckObj, 'requestbody') ? _.get(req, ownerCheckObj.requestbody) : null - if (comparedWith === comparedFrom) { + + if ((_.isArray(comparedWith) && _.isArray(comparedFrom)) && (_.intersection(comparedWith, comparedFrom).length > 0)) { + resolve() + } else if (_.isArray(comparedWith) && (_.includes(comparedWith, comparedFrom))) { + resolve() + } else if (_.isArray(comparedFrom) && (_.includes(comparedFrom, comparedWith))) { + resolve() + } else if (comparedWith === comparedFrom) { resolve() } else { return reject('Mismatch in param validation') From f75a40fc7bbc633ccae4900af54b7f09b08b7f70 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Mon, 8 Nov 2021 11:22:51 +0530 Subject: [PATCH 166/856] Added API to get BudgetScheme --- src/utils/whitelistApis.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index b070cdda..be12613b 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1275,6 +1275,12 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.PUBLIC, ], + }, '/proxies/v8/budget/scheme/:orgId/:budgetYear': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], }, '/proxies/v8/orghistory/:orgId/:key': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1464,6 +1470,7 @@ export const API_LIST = { '/proxies/v8/staff/position/:orgId', '/proxies/v8/budget/scheme', '/proxies/v8/budget/scheme/:orgId', + '/proxies/v8/budget/scheme/:orgId/:budgetYear', '/proxies/v8/orghistory/:orgId/:key', ], } From 4155046f344e6eab439a94c892bbfb16760e2bf5 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Mon, 8 Nov 2021 14:29:15 +0530 Subject: [PATCH 167/856] Added api to whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index be12613b..8d0d06cc 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1288,6 +1288,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/discussion/user/uid/:uid': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -1472,5 +1479,6 @@ export const API_LIST = { '/proxies/v8/budget/scheme/:orgId', '/proxies/v8/budget/scheme/:orgId/:budgetYear', '/proxies/v8/orghistory/:orgId/:key', + '/proxies/v8/discussion/user/uid/:uid', ], } From d15e81772e7b2c3d4ca014a76ce9729fe49f00e0 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Mon, 8 Nov 2021 17:06:44 +0530 Subject: [PATCH 168/856] Participant list for batch --- src/protectedApi_v8/cohorts.ts | 65 +++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/src/protectedApi_v8/cohorts.ts b/src/protectedApi_v8/cohorts.ts index 4f84acbd..cdc11421 100644 --- a/src/protectedApi_v8/cohorts.ts +++ b/src/protectedApi_v8/cohorts.ts @@ -9,14 +9,15 @@ import { extractAuthorizationFromRequest, extractUserIdFromRequest, extractUserT const API_END_POINTS = { addTemplate: `https://igot-dev.in/api/course/batch/cert/v1/template/add`, autoenrollment: `${CONSTANTS.KONG_API_BASE}/v1/autoenrollment`, + batchParticipantsApi: `${CONSTANTS.KONG_API_BASE}/course/v1/batch/participants/list`, cohorts: `${CONSTANTS.KONG_API_BASE}/v2/resources`, downloadCert: (certId: string) => `https://igot-dev.in/api/certreg/v2/certs/download/${certId}`, groupCohorts: (groupId: number) => `${CONSTANTS.USER_PROFILE_API_BASE}/groups/${groupId}/users `, hierarchyApiEndPoint: (contentId: string) => `${CONSTANTS.KNOWLEDGE_MW_API_BASE}/action/content/v3/hierarchy/${contentId}?hierarchyType=detail`, - issueCert: 'https://igot-dev.in/api/course/batch/cert/v1/issue?reIssue=true', - + issueCert: 'https://igot-dev.in/api/course/batch/cert/v1/issue?reIssue=true', + kongSearchUser: `${CONSTANTS.KONG_API_BASE}/user/v1/search`, searchUserRegistry: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/search/profile`, } const VALID_COHORT_TYPES = new Set([ @@ -199,7 +200,7 @@ cohortsApi.post('/course/batch/cert/issue', async (req, res) => { headers: { Authorization: CONSTANTS.CERT_AUTH_TOKEN, /* tslint:disable-next-line */ - 'x-authenticated-user-token' : extractUserToken(req), + 'x-authenticated-user-token': extractUserToken(req), }, }) @@ -223,7 +224,7 @@ cohortsApi.get('/course/batch/cert/download/:certId', async (req, res) => { headers: { Authorization: CONSTANTS.CERT_AUTH_TOKEN, /* tslint:disable-next-line */ - 'x-authenticated-user-token' : extractUserToken(req), + 'x-authenticated-user-token': extractUserToken(req), }, }) @@ -239,11 +240,61 @@ cohortsApi.get('/course/batch/cert/download/:certId', async (req, res) => { } }) +cohortsApi.get('/getUsersForBatch/:batchId', async (req, res) => { + try { + const batchId = req.params.batchId + const reqBody = { + request: { + batch: { + active: true, + batchId, + }, + }, + } + const userlist: ICohortsUser[] = [] + const response = await axios.post(API_END_POINTS.batchParticipantsApi, reqBody, { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + /* tslint:disable-next-line */ + 'x-authenticated-user-token': extractUserToken(req), + }, + }) + if ((typeof response.data.result.batch.participants !== 'undefined' && response.data.result.batch.participants.length > 0)) { + const searchresponse = await axios({ + ...axiosRequestConfig, + data: { request: {filters: { userId: response.data.result.batch.participants } } }, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), + }, + method: 'POST', + url: API_END_POINTS.kongSearchUser, + }) + if (searchresponse.data.result.response.count > 0) { + for (const profileObj of searchresponse.data.result.response.content) { + userlist.push(getUsers(profileObj.profileDetails)) + } + } + } + res.status(response.status).send(userlist) + } catch (err) { + logError(err) + + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: unknownError, + } + ) + } +}) + function getUsers(userprofile: IUserProfile): ICohortsUser { return { city: '', - department: userprofile.professionalDetails[0].name, + department: userprofile.employmentDetails.departmentName, desc: '', designation: userprofile.professionalDetails[0].designation, email: userprofile.personalDetails.primaryEmail, @@ -271,6 +322,7 @@ export interface ICohortsUser { export interface IUserProfile { personalDetails: IPersonalDetails professionalDetails: IProfessionalDetailsEntity[] + employmentDetails: IEmploymentDetails id: string } export interface IPersonalDetails { @@ -291,6 +343,9 @@ export interface IPersonalDetails { personalEmail: string } +export interface IEmploymentDetails { + departmentName: string +} export interface IProfessionalDetailsEntity { description: string industry: string From a81f9e9a3aca3c5b50c70f265d1707449bcfa6bd Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Mon, 8 Nov 2021 17:27:57 +0530 Subject: [PATCH 169/856] API End Point Issue --- src/protectedApi_v8/cohorts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protectedApi_v8/cohorts.ts b/src/protectedApi_v8/cohorts.ts index cdc11421..c180afd6 100644 --- a/src/protectedApi_v8/cohorts.ts +++ b/src/protectedApi_v8/cohorts.ts @@ -240,7 +240,7 @@ cohortsApi.get('/course/batch/cert/download/:certId', async (req, res) => { } }) -cohortsApi.get('/getUsersForBatch/:batchId', async (req, res) => { +cohortsApi.get('/course/getUsersForBatch/:batchId', async (req, res) => { try { const batchId = req.params.batchId const reqBody = { From ddfd547f01463bd718f13691174779e6bdb0830f Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Mon, 8 Nov 2021 17:36:01 +0530 Subject: [PATCH 170/856] API Access Issue --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index be12613b..f9770b86 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -954,6 +954,13 @@ export const API_LIST = { // ROLE.PUBLIC, // ], // }, + '/protected/v8/cohorts/course/getUsersForBatch/:courseId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/cohorts/user/autoenrollment/:courseId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1427,6 +1434,7 @@ export const API_LIST = { // '/protected/v8/connections/connections/requested', // '/protected/v8/connections/add/connection', // '/protected/v8/connections/connections/suggests', + '/protected/v8/cohorts/course/getUsersForBatch/:courseId', '/protected/v8/cohorts/user/autoenrollment/:courseId', '/protected/v8/profanity/startPdfProfanity', '/protected/v8/profanity/getPdfProfanityForContent/:contentId', From a88e69396870ab661d7ca7d32721f5f00f412cae Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Mon, 8 Nov 2021 18:00:59 +0530 Subject: [PATCH 171/856] Profile Details Issue --- src/protectedApi_v8/cohorts.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/protectedApi_v8/cohorts.ts b/src/protectedApi_v8/cohorts.ts index c180afd6..79534cc3 100644 --- a/src/protectedApi_v8/cohorts.ts +++ b/src/protectedApi_v8/cohorts.ts @@ -294,9 +294,9 @@ function getUsers(userprofile: IUserProfile): ICohortsUser { return { city: '', - department: userprofile.employmentDetails.departmentName, + department: userprofile.employmentDetails === undefined ? '' : userprofile.employmentDetails.departmentName, desc: '', - designation: userprofile.professionalDetails[0].designation, + designation: (userprofile.professionalDetails === undefined || userprofile.professionalDetails.length<1) ? '' : userprofile.professionalDetails[0].designation, email: userprofile.personalDetails.primaryEmail, first_name: userprofile.personalDetails.firstname, last_name: userprofile.personalDetails.middlename, From da514abab447fa7df78f3253639fd658fa26c7b9 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Mon, 8 Nov 2021 18:02:07 +0530 Subject: [PATCH 172/856] Profile Details Issue --- src/protectedApi_v8/cohorts.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/protectedApi_v8/cohorts.ts b/src/protectedApi_v8/cohorts.ts index 79534cc3..9653c50c 100644 --- a/src/protectedApi_v8/cohorts.ts +++ b/src/protectedApi_v8/cohorts.ts @@ -296,7 +296,8 @@ function getUsers(userprofile: IUserProfile): ICohortsUser { city: '', department: userprofile.employmentDetails === undefined ? '' : userprofile.employmentDetails.departmentName, desc: '', - designation: (userprofile.professionalDetails === undefined || userprofile.professionalDetails.length<1) ? '' : userprofile.professionalDetails[0].designation, + designation: (userprofile.professionalDetails === undefined || userprofile.professionalDetails.length < 1) + ? '' : userprofile.professionalDetails[0].designation, email: userprofile.personalDetails.primaryEmail, first_name: userprofile.personalDetails.firstname, last_name: userprofile.personalDetails.middlename, From 7eb8be19bfda9bff91268979c1f4b8aa6b8fa7d6 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Tue, 9 Nov 2021 14:08:51 +0530 Subject: [PATCH 173/856] Designation & Department Value Changes --- src/protectedApi_v8/cohorts.ts | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/protectedApi_v8/cohorts.ts b/src/protectedApi_v8/cohorts.ts index 9653c50c..8bf0d33c 100644 --- a/src/protectedApi_v8/cohorts.ts +++ b/src/protectedApi_v8/cohorts.ts @@ -263,20 +263,22 @@ cohortsApi.get('/course/getUsersForBatch/:batchId', async (req, res) => { if ((typeof response.data.result.batch.participants !== 'undefined' && response.data.result.batch.participants.length > 0)) { const searchresponse = await axios({ ...axiosRequestConfig, - data: { request: {filters: { userId: response.data.result.batch.participants } } }, + data: { request: { filters: { userId: response.data.result.batch.participants } } }, headers: { - Authorization: CONSTANTS.SB_API_KEY, - // tslint:disable-next-line: all - 'x-authenticated-user-token': extractUserToken(req), + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), }, method: 'POST', url: API_END_POINTS.kongSearchUser, - }) + }) if (searchresponse.data.result.response.count > 0) { for (const profileObj of searchresponse.data.result.response.content) { - userlist.push(getUsers(profileObj.profileDetails)) + const user: ICohortsUser = getUsers(profileObj.profileDetails) + user.department = profileObj.channel + userlist.push(user) } - } + } } res.status(response.status).send(userlist) } catch (err) { @@ -291,13 +293,19 @@ cohortsApi.get('/course/getUsersForBatch/:batchId', async (req, res) => { }) function getUsers(userprofile: IUserProfile): ICohortsUser { - + let designationValue = '' + if (userprofile.professionalDetails !== undefined && userprofile.professionalDetails.length > 0) { + if (userprofile.professionalDetails[0].designationOther !== undefined) { + designationValue = userprofile.professionalDetails[0].designationOther + } else { + designationValue = userprofile.professionalDetails[0].designation + } + } return { city: '', department: userprofile.employmentDetails === undefined ? '' : userprofile.employmentDetails.departmentName, desc: '', - designation: (userprofile.professionalDetails === undefined || userprofile.professionalDetails.length < 1) - ? '' : userprofile.professionalDetails[0].designation, + designation: designationValue, email: userprofile.personalDetails.primaryEmail, first_name: userprofile.personalDetails.firstname, last_name: userprofile.personalDetails.middlename, From ff31b08bcb74de0798fe4482e0ed8c06a9745dc8 Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Tue, 9 Nov 2021 14:27:17 +0530 Subject: [PATCH 174/856] Designation value change --- src/protectedApi_v8/cohorts.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/protectedApi_v8/cohorts.ts b/src/protectedApi_v8/cohorts.ts index 8bf0d33c..c7a6c482 100644 --- a/src/protectedApi_v8/cohorts.ts +++ b/src/protectedApi_v8/cohorts.ts @@ -295,10 +295,10 @@ cohortsApi.get('/course/getUsersForBatch/:batchId', async (req, res) => { function getUsers(userprofile: IUserProfile): ICohortsUser { let designationValue = '' if (userprofile.professionalDetails !== undefined && userprofile.professionalDetails.length > 0) { - if (userprofile.professionalDetails[0].designationOther !== undefined) { - designationValue = userprofile.professionalDetails[0].designationOther - } else { + if (userprofile.professionalDetails[0].designation !== undefined) { designationValue = userprofile.professionalDetails[0].designation + } else { + designationValue = userprofile.professionalDetails[0].designationOther } } return { From d5dbee63d5e19946d00bbd2ccd5a26639545195e Mon Sep 17 00:00:00 2001 From: amit-kumaryadav Date: Tue, 9 Nov 2021 14:56:22 +0530 Subject: [PATCH 175/856] Designation value change --- src/protectedApi_v8/cohorts.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/protectedApi_v8/cohorts.ts b/src/protectedApi_v8/cohorts.ts index c7a6c482..f58b4878 100644 --- a/src/protectedApi_v8/cohorts.ts +++ b/src/protectedApi_v8/cohorts.ts @@ -298,7 +298,8 @@ function getUsers(userprofile: IUserProfile): ICohortsUser { if (userprofile.professionalDetails[0].designation !== undefined) { designationValue = userprofile.professionalDetails[0].designation } else { - designationValue = userprofile.professionalDetails[0].designationOther + designationValue = userprofile.professionalDetails[0].designationOther === undefined ? '' : + userprofile.professionalDetails[0].designationOther } } return { From 3e8de1b3c448969ea1a1ac71da53826865d59281 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Wed, 10 Nov 2021 00:05:21 +0530 Subject: [PATCH 176/856] added discussion forum api to whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 95215f72..38cb47d4 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1302,6 +1302,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/discussion/user/:username/posts': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -1488,5 +1495,6 @@ export const API_LIST = { '/proxies/v8/budget/scheme/:orgId/:budgetYear', '/proxies/v8/orghistory/:orgId/:key', '/proxies/v8/discussion/user/uid/:uid', + '/proxies/v8/discussion/user/:username/posts' ], } From a83d1da9f3af6128e0a35db02ec7fb0a873650d2 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Wed, 10 Nov 2021 14:51:01 +0530 Subject: [PATCH 177/856] Added APIs for Category & Terms --- src/utils/whitelistApis.ts | 58 +++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 38cb47d4..a5dc712c 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1309,6 +1309,55 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/action/framework/v3/read/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/action/framework/v3/category/master/create': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/action/framework/v3/category/master/search': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/action/framework/v3/category/master/read/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + 'proxies/v8/action/framework/v3/category/create': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/action/framework/v3/category/read/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + 'proxies/v8/action/framework/v3/term/create': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -1495,6 +1544,13 @@ export const API_LIST = { '/proxies/v8/budget/scheme/:orgId/:budgetYear', '/proxies/v8/orghistory/:orgId/:key', '/proxies/v8/discussion/user/uid/:uid', - '/proxies/v8/discussion/user/:username/posts' + '/proxies/v8/discussion/user/:username/posts', + '/proxies/v8/action/framework/v3/read/:id', + '/proxies/v8/action/framework/v3/category/master/create', + '/proxies/v8/action/framework/v3/category/master/search', + '/proxies/v8/action/framework/v3/category/master/read/:id', + '/proxies/v8/action/framework/v3/category/create', + '/proxies/v8/action/framework/v3/category/read/:id', + '/proxies/v8/action/framework/v3/term/create' ], } From 8420eb8de1816f2c17f3413cda80a5a19f80f34b Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Wed, 10 Nov 2021 15:38:55 +0530 Subject: [PATCH 178/856] Added retire API for framework category --- src/utils/whitelistApis.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index a5dc712c..6f26cd4e 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1337,7 +1337,7 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - 'proxies/v8/action/framework/v3/category/create': { + '/proxies/v8/action/framework/v3/category/create': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -1351,6 +1351,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/action/framework/v3/category/retire/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, 'proxies/v8/action/framework/v3/term/create': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1551,6 +1558,7 @@ export const API_LIST = { '/proxies/v8/action/framework/v3/category/master/read/:id', '/proxies/v8/action/framework/v3/category/create', '/proxies/v8/action/framework/v3/category/read/:id', + '/proxies/v8/action/framework/v3/category/retire/:id', '/proxies/v8/action/framework/v3/term/create' ], } From cfe9e6e013d9b7f2e1ab51dab957abc90eea282d Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Wed, 10 Nov 2021 22:13:45 +0530 Subject: [PATCH 179/856] Added api to whitelist --- src/utils/whitelistApis.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 6f26cd4e..43851415 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1358,7 +1358,14 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - 'proxies/v8/action/framework/v3/term/create': { + '/proxies/v8/action/framework/v3/term/create': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/action/content/v3/upload/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -1559,6 +1566,7 @@ export const API_LIST = { '/proxies/v8/action/framework/v3/category/create', '/proxies/v8/action/framework/v3/category/read/:id', '/proxies/v8/action/framework/v3/category/retire/:id', - '/proxies/v8/action/framework/v3/term/create' + '/proxies/v8/action/framework/v3/term/create', + '/proxies/v8/action/content/v3/upload/:do_id', ], } From 2d1afee267c2a7ca57d84e8bfab1427fda2211d7 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Thu, 11 Nov 2021 16:38:48 +0530 Subject: [PATCH 180/856] Added phase 1 role checks . refs. #56095 --- src/protectedApi_v8/cohorts.ts | 2 +- src/utils/whitelistApis.ts | 251 ++++++++++++++++++++++----------- 2 files changed, 172 insertions(+), 81 deletions(-) diff --git a/src/protectedApi_v8/cohorts.ts b/src/protectedApi_v8/cohorts.ts index f58b4878..07e4f629 100644 --- a/src/protectedApi_v8/cohorts.ts +++ b/src/protectedApi_v8/cohorts.ts @@ -298,7 +298,7 @@ function getUsers(userprofile: IUserProfile): ICohortsUser { if (userprofile.professionalDetails[0].designation !== undefined) { designationValue = userprofile.professionalDetails[0].designation } else { - designationValue = userprofile.professionalDetails[0].designationOther === undefined ? '' : + designationValue = userprofile.professionalDetails[0].designationOther === undefined ? '' : userprofile.professionalDetails[0].designationOther } } diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 43851415..7a8a7ab7 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -80,14 +80,14 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/event/v4/create': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/user/v1/read/:id': { @@ -178,7 +178,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_REVIEWER, ], }, '/proxies/v8/discussion/forum/v2/read': { @@ -223,102 +223,116 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/proxies/v8/action/content/v3/hierarchyUpdate': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, + // '/proxies/v8/action/content/v3/hierarchyUpdate': { + // checksNeeded: [CHECK.ROLE], + // // tslint:disable-next-line: object-literal-sort-keys + // ROLE_CHECK: [ + // ROLE.PUBLIC, + // ], + // }, '/proxies/v8/action/content/v3/hierarchy/update': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, ], }, '/proxies/v8/action/content/v3/create': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, ], }, '/proxies/v8/dashboard/analytics/getDashboardConfig/Karmayogi/:comp': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/dashboard/analytics/getDashboardsForProfile/Karmayogi': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/dashboard/analytics/getChartV2/Karmayogi': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/wat/dashboard/getDashboardConfig/Karmayogi/overview': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/upload/action/content/v3/upload/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, ], }, '/proxies/v8/v1/content/retire': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, ], }, '/proxies/v8/action/content/v3/updateReviewStatus/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_REVIEWER, ], }, '/proxies/v8/action/content/v3/review/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_REVIEWER, ], }, '/proxies/v8/action/content/v3/publish/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_PUBLISHER, ], }, '/proxies/v8/action/content/v3/update/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, ], }, '/proxies/v8/data/v1/system/settings/get/orgTypeList': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/org/v1/search': { @@ -332,7 +346,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/user/v1/search': { @@ -346,35 +360,41 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/org/v1/update': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/user/v1/block': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/user/v1/unblock': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/data/v1/system/settings/get/orgTypeConfig': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/discussion/tags': { @@ -388,21 +408,23 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/learnervm/private/content/v3/publish/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_PUBLISHER, ], }, '/proxies/v8/learnervm/private/content/v3/review/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_REVIEWER, ], }, '/proxies/v8/discussion/v2/topics': { @@ -472,14 +494,18 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/user/private/v1/migrate': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/user/private/v1/assign/role/userrole': { @@ -521,14 +547,16 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, ], }, '/proxies/v8/private/content/v3/update/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, ], }, '/proxies/v8/notifyContentState': { @@ -563,7 +591,8 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, ], }, '/protected/v8/user/content/like': { @@ -619,21 +648,23 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, + ROLE.MDO_ADMIN, ], }, '/protected/v8/portal/spv/department': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.SPV_ADMIN, ], }, '/protected/v8/portal/spv/deptAction/': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.SPV_ADMIN, ], }, '/protected/v8/user/roles/getUsersV2': { @@ -647,21 +678,21 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.SPV_ADMIN, ], }, '/protected/v8/portal/mdo/mydepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/protected/v8/user/profileDetails/createUser': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/protected/v8/user/profileRegistry/getUserRegistryByUser/:id': { @@ -689,14 +720,19 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workflowhandler/historyByApplicationId/:applicationId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/user/autocomplete/:query': { @@ -710,7 +746,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.SPV_ADMIN, ], }, '/protected/v8/user/preference': { @@ -724,28 +760,40 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workallocation/getWorkOrders': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workallocation/add/workorder': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workallocation/getWorkOrderById/:workOrderId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], SCOPE_CHECK: [ ROLE.MDO_ADMIN, @@ -790,35 +838,47 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workallocation/getWorkAllocationById/:workAllocationId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workallocation/update/workorder': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workallocation/getWOPdf/:workOrderId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/portal/cbp/mydepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, ], }, '/protected/v8/user/mandatoryContent/checkStatus': { @@ -881,28 +941,29 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, ], }, '/protected/v8/portal/cbc/department/:deptId/': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, ], }, '/protected/v8/portal/spv/department/:deptId/': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.SPV_ADMIN, ], }, '/protected/v8/scroing/calculate': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, ], }, // '/protected/v8/connections/connections/recommended': { @@ -972,42 +1033,49 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, ], }, '/protected/v8/profanity/getPdfProfanityForContent/:contentId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, ], }, '/protected/v8/catalog': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, ], }, '/protected/v8/scroing/fetch': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, ], }, '/protected/v8/portal/mdo/deptAction/userrole': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/protected/v8/workallocation/v2/update': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/user/profileDetails/updateUser': { @@ -1042,7 +1110,10 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/user/details/detailV1': { @@ -1098,35 +1169,50 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workflowhandler/historyByApplicationIdAndWfId/:applicationId/:wfId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workflowhandler/workflowProcess/:wfId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workflowhandler/updateUserProfileWf': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workflowhandler/userWfSearch': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/user/profileRegistry/getUserRegistryById': { @@ -1147,7 +1233,10 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/reset': { @@ -1236,7 +1325,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/authApi/batch/:key': { @@ -1262,37 +1351,39 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/staff/position/:orgId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/budget/scheme': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/budget/scheme/:orgId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/budget/scheme/:orgId/:budgetYear': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/orghistory/:orgId/:key': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/discussion/user/uid/:uid': { @@ -1402,7 +1493,7 @@ export const API_LIST = { '/proxies/v8/read/content-progres/:do_id', '/proxies/v8/content-progres/:do_id', '/proxies/v8/action/content/v3/hierarchy/:do_id', - '/proxies/v8/action/content/v3/hierarchyUpdate', + // '/proxies/v8/action/content/v3/hierarchyUpdate', '/proxies/v8/action/content/v3/hierarchy/update', '/proxies/v8/action/content/v3/read/:do_id', '/proxies/v8/discussion/forum/v2/read', From a9874f7b46c7b91ffb15f5ccc9187ba37aa60de0 Mon Sep 17 00:00:00 2001 From: Amit Sengar Date: Fri, 12 Nov 2021 20:22:53 +0530 Subject: [PATCH 181/856] Update whitelistApis.ts --- src/utils/whitelistApis.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 7a8a7ab7..a88e1388 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -308,6 +308,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ + ROLE.CONTENT_CREATOR, ROLE.CONTENT_REVIEWER, ], }, From ad1d07922e00287054d18711ddb5f43ef1da231c Mon Sep 17 00:00:00 2001 From: Amit Sengar Date: Fri, 12 Nov 2021 21:10:22 +0530 Subject: [PATCH 182/856] Update whitelistApis.ts --- src/utils/whitelistApis.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index a88e1388..9324d2d0 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1059,6 +1059,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.CONTENT_CREATOR, + ROLE.CONTENT_REVIEWER, ROLE.EDITOR, ], }, From 4e8e3e7adb44bc5c27324b331da4afe7ae387f9d Mon Sep 17 00:00:00 2001 From: Amit Sengar Date: Fri, 12 Nov 2021 21:23:55 +0530 Subject: [PATCH 183/856] Update whitelistApis.ts --- src/utils/whitelistApis.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 9324d2d0..b0452147 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1043,6 +1043,8 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.CONTENT_CREATOR, + ROLE.CONTENT_REVIEWER, + ROLE.CONTENT_PUBLISHER, ROLE.EDITOR, ], }, From bf905d3a122c1428d73c56de8fc02a41d3a78b72 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Mon, 15 Nov 2021 16:09:24 +0530 Subject: [PATCH 184/856] Added role to apis --- src/utils/whitelistApis.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index b0452147..094540ac 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -179,6 +179,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.CONTENT_REVIEWER, + ROLE.CONTENT_PUBLISHER, ], }, '/proxies/v8/discussion/forum/v2/read': { @@ -236,6 +237,8 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, ROLE.EDITOR, + ROLE.CONTENT_PUBLISHER, + ROLE.CONTENT_REVIEWER, ], }, '/proxies/v8/action/content/v3/create': { @@ -302,6 +305,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.CONTENT_REVIEWER, + ROLE.CONTENT_PUBLISHER, ], }, '/proxies/v8/action/content/v3/review/:do_id': { @@ -310,6 +314,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, ROLE.CONTENT_REVIEWER, + ROLE.CONTENT_PUBLISHER, ], }, '/proxies/v8/action/content/v3/publish/:do_id': { @@ -325,6 +330,8 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, ROLE.EDITOR, + ROLE.CONTENT_REVIEWER, + ROLE.CONTENT_PUBLISHER, ], }, '/proxies/v8/data/v1/system/settings/get/orgTypeList': { @@ -965,6 +972,8 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, ROLE.EDITOR, + ROLE.CONTENT_PUBLISHER, + ROLE.CONTENT_REVIEWER, ], }, // '/protected/v8/connections/connections/recommended': { @@ -1036,6 +1045,8 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, ROLE.EDITOR, + ROLE.CONTENT_PUBLISHER, + ROLE.CONTENT_REVIEWER, ], }, '/protected/v8/profanity/getPdfProfanityForContent/:contentId': { @@ -1063,6 +1074,7 @@ export const API_LIST = { ROLE.CONTENT_CREATOR, ROLE.CONTENT_REVIEWER, ROLE.EDITOR, + ROLE.CONTENT_PUBLISHER, ], }, '/protected/v8/portal/mdo/deptAction/userrole': { From 172d159878f170a6dea7db70570f38983b81e281 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Mon, 15 Nov 2021 17:14:40 +0530 Subject: [PATCH 185/856] hierarchy update added --- src/utils/whitelistApis.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 094540ac..4b7834a6 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -224,13 +224,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - // '/proxies/v8/action/content/v3/hierarchyUpdate': { - // checksNeeded: [CHECK.ROLE], - // // tslint:disable-next-line: object-literal-sort-keys - // ROLE_CHECK: [ - // ROLE.PUBLIC, - // ], - // }, + '/proxies/v8/action/content/v3/hierarchyUpdate': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.CONTENT_REVIEWER, + ], + }, '/proxies/v8/action/content/v3/hierarchy/update': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1509,7 +1509,7 @@ export const API_LIST = { '/proxies/v8/read/content-progres/:do_id', '/proxies/v8/content-progres/:do_id', '/proxies/v8/action/content/v3/hierarchy/:do_id', - // '/proxies/v8/action/content/v3/hierarchyUpdate', + '/proxies/v8/action/content/v3/hierarchyUpdate', '/proxies/v8/action/content/v3/hierarchy/update', '/proxies/v8/action/content/v3/read/:do_id', '/proxies/v8/discussion/forum/v2/read', From 3b34899730ab2409f8733fad821b553942c101c7 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Tue, 16 Nov 2021 18:56:46 +0530 Subject: [PATCH 186/856] Added api to whitelist --- src/utils/whitelistApis.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 4b7834a6..c20fc963 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -595,6 +595,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/cohorts/authors/:do_id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/cohorts/:cohortType/:contentId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1065,6 +1072,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, ROLE.EDITOR, + ROLE.PUBLIC, ], }, '/protected/v8/scroing/fetch': { @@ -1675,5 +1683,6 @@ export const API_LIST = { '/proxies/v8/action/framework/v3/category/retire/:id', '/proxies/v8/action/framework/v3/term/create', '/proxies/v8/action/content/v3/upload/:do_id', + '/protected/v8/cohorts/authors/:do_id', ], } From 9fcc5c231b268fc4e0bd265281de5055cbf0b5de Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Tue, 16 Nov 2021 19:31:46 +0530 Subject: [PATCH 187/856] Added the api to whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index c20fc963..966104dc 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1066,6 +1066,13 @@ export const API_LIST = { ROLE.EDITOR, ], }, + '/protected/v8/cohorts/activeusers/:do_id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/catalog': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1684,5 +1691,6 @@ export const API_LIST = { '/proxies/v8/action/framework/v3/term/create', '/proxies/v8/action/content/v3/upload/:do_id', '/protected/v8/cohorts/authors/:do_id', + '/protected/v8/cohorts/activeusers/:do_id', ], } From c8dc699988d427354505106f0b0cc04d0c21e28d Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Tue, 16 Nov 2021 20:01:53 +0530 Subject: [PATCH 188/856] Changes for roles --- src/utils/whitelistApis.ts | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 966104dc..1410e777 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -595,19 +595,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/protected/v8/cohorts/authors/:do_id': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, '/protected/v8/cohorts/:cohortType/:contentId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.CONTENT_CREATOR, ROLE.EDITOR, + ROLE.PUBLIC, ], }, '/protected/v8/user/content/like': { @@ -1066,13 +1060,6 @@ export const API_LIST = { ROLE.EDITOR, ], }, - '/protected/v8/cohorts/activeusers/:do_id': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, '/protected/v8/catalog': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1690,7 +1677,5 @@ export const API_LIST = { '/proxies/v8/action/framework/v3/category/retire/:id', '/proxies/v8/action/framework/v3/term/create', '/proxies/v8/action/content/v3/upload/:do_id', - '/protected/v8/cohorts/authors/:do_id', - '/protected/v8/cohorts/activeusers/:do_id', ], } From acb06ac0744b3709e3fadedfaf2ff8a709567f30 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Wed, 17 Nov 2021 10:26:12 +0530 Subject: [PATCH 189/856] Added api to whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 1410e777..a76cf660 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1481,6 +1481,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/learner/course/v1/batch/list': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -1677,5 +1684,6 @@ export const API_LIST = { '/proxies/v8/action/framework/v3/category/retire/:id', '/proxies/v8/action/framework/v3/term/create', '/proxies/v8/action/content/v3/upload/:do_id', + '/proxies/v8/learner/course/v1/batch/list', ], } From 7a357f524da61bae374f4d2a66b99ee3e28dc41b Mon Sep 17 00:00:00 2001 From: Amit Sengar Date: Thu, 18 Nov 2021 18:46:53 +0530 Subject: [PATCH 190/856] Update whitelistApis.ts --- src/utils/whitelistApis.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index a76cf660..dfac3175 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -702,6 +702,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/user/profileRegistry/getUserRegistryByUser/:id': { From 930440a6ff92aa1a316b98946955add388866cff Mon Sep 17 00:00:00 2001 From: SuvajitTarento <72616697+SuvajitTarento@users.noreply.github.com> Date: Fri, 19 Nov 2021 11:45:40 +0530 Subject: [PATCH 191/856] Added role to API --- src/utils/whitelistApis.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index dfac3175..24366bd3 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -355,6 +355,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/user/v1/search': { From d745128797cbf898caac2ce29e37c97d70702add Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Fri, 19 Nov 2021 13:02:00 +0530 Subject: [PATCH 192/856] added api's to whitelist --- src/utils/env.ts | 2 +- src/utils/whitelistApis.ts | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/utils/env.ts b/src/utils/env.ts index d3b5a3fb..5f8a55e4 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -154,7 +154,7 @@ export const CONSTANTS = { CONTENT_SERVICE_API_BASE: env.CONTENT_SERVICE_API_BASE || 'http://content-service:9000', VM_LEARNING_SERVICE_URL: env.VM_LEARNING_SERVICE_URL, // tslint:disable-next-line: max-line-length - CERT_AUTH_TOKEN: 'bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJRekw4VVA1dUtqUFdaZVpMd1ZtTFJvNHdqWTg2a2FrcSJ9.TPjV0xLacSbp3FbJ7XeqHoKFN35Rl4YHx3DZNN9pm0o', + CERT_AUTH_TOKEN: 'bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJVMkpUdlpERFY4eG83ZmtfNHd1Yy1kNVJmNjRPTG1oemlRRUhjR25Vc2hNIn0.eyJqdGkiOiIyM2YwMzUyOC1lNTUwLTQ2OGQtOWRkYy0yZDMwNDJkNjliMjQiLCJleHAiOjE2MzczOTMzMzksIm5iZiI6MCwiaWF0IjoxNjM3MzA2OTM5LCJpc3MiOiJodHRwczovL2lnb3QtZGV2LmluL2F1dGgvcmVhbG1zL3N1bmJpcmQiLCJhdWQiOiJhZG1pbi1jbGkiLCJzdWIiOiJmOjkyM2JkYzE4LTUyMGQtNDhkNC1hODRlLTNjZGUxZTY1NWViZDo3NTg1NGI0ZC01ZWMyLTRlYzMtYTE4ZC05NTIwMzAzODQzNjEiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhZG1pbi1jbGkiLCJhdXRoX3RpbWUiOjAsInNlc3Npb25fc3RhdGUiOiI4MGNiOTg4OC00MzMzLTQ4YzMtYmIyZS00ZTA5MjUyOWU1OTYiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnt9LCJuYW1lIjoiYmVzY29tIGNvbnRlbnQiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJiZXNjb21jb250ZW50X3EzZjMiLCJnaXZlbl9uYW1lIjoiYmVzY29tIiwiZmFtaWx5X25hbWUiOiJjb250ZW50IiwiZW1haWwiOiJiZSoqKioqKioqKioqKkB5b3BtYWlsLmNvbSJ9.jwBhB-I2zZKArkIv-NdDfgNoYLdofaq-zR8sfSR5eMW25uHNcWQnBHIdA7CQp02bP_2RP2Zw1PF4S2PrJM8r5OVyRjEOx-R1LPun8I7BtQIb4k5vV9DupZack5V8HB800x9kbCCgZxxBWAT4ud7UmmQkzX-gIWw4XPWuxY4mTy7BhVhtW88IkfDB7GPxmuUGBOUJQ3ZOehDg8sWHGpDfF8ZEf3M_XHeeEap23iviFGlcpcb4uOi9SDsTpU9MaeoN4skC6fvr9SUD3WJo276ateGnJElyflckLQhhvX23rROtCif4Ouip-o3vtjGblaaTdcNbPw10z-74wNIraM7yxA', } diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 38cb47d4..aebc38f0 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1309,6 +1309,30 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + // '/protected/v8/cohorts/course/batch/cert/download/', + // '/protected/v8/cohorts/course/batch/cert/issue', + // '/protected/v8/cohorts/course/batch/cert/template/add', + '/protected/v8/cohorts/course/batch/cert/download/:certId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/cohorts/course/batch/cert/issue': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/cohorts/course/batch/cert/template/add': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -1487,6 +1511,9 @@ export const API_LIST = { '/protected/v8/connections/v2/connections/suggests', '/protected/v8/connections/v2/update/connection', '/protected/v8/user/profileDetails/createUserWithoutInvitationEmail', + '/protected/v8/cohorts/course/batch/cert/download/:certId', + '/protected/v8/cohorts/course/batch/cert/issue', + '/protected/v8/cohorts/course/batch/cert/template/add', '/proxies/v8/searchBy/:key', '/proxies/v8/staff/position', '/proxies/v8/staff/position/:orgId', From e266c21053f0ba2088718501bf054ec01267871d Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Fri, 19 Nov 2021 13:32:54 +0530 Subject: [PATCH 193/856] Revert "Merge branch 'cbrelease-3.0.1' of https://github.com/sunbird-cb/sunbird-cb-uiproxy into cbrelease-3.0.1" This reverts commit f7761656d67c252be6cd1a79338172da823276d1, reversing changes made to d745128797cbf898caac2ce29e37c97d70702add. --- src/protectedApi_v8/cohorts.ts | 2 +- src/utils/whitelistApis.ts | 335 +++++++-------------------------- 2 files changed, 73 insertions(+), 264 deletions(-) diff --git a/src/protectedApi_v8/cohorts.ts b/src/protectedApi_v8/cohorts.ts index 07e4f629..f58b4878 100644 --- a/src/protectedApi_v8/cohorts.ts +++ b/src/protectedApi_v8/cohorts.ts @@ -298,7 +298,7 @@ function getUsers(userprofile: IUserProfile): ICohortsUser { if (userprofile.professionalDetails[0].designation !== undefined) { designationValue = userprofile.professionalDetails[0].designation } else { - designationValue = userprofile.professionalDetails[0].designationOther === undefined ? '' : + designationValue = userprofile.professionalDetails[0].designationOther === undefined ? '' : userprofile.professionalDetails[0].designationOther } } diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 40466760..aebc38f0 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -80,14 +80,14 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/event/v4/create': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/user/v1/read/:id': { @@ -178,8 +178,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_REVIEWER, - ROLE.CONTENT_PUBLISHER, + ROLE.PUBLIC, ], }, '/proxies/v8/discussion/forum/v2/read': { @@ -228,119 +227,98 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_REVIEWER, + ROLE.PUBLIC, ], }, '/proxies/v8/action/content/v3/hierarchy/update': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.EDITOR, - ROLE.CONTENT_PUBLISHER, - ROLE.CONTENT_REVIEWER, + ROLE.PUBLIC, ], }, '/proxies/v8/action/content/v3/create': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, + ROLE.PUBLIC, ], }, '/proxies/v8/dashboard/analytics/getDashboardConfig/Karmayogi/:comp': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/dashboard/analytics/getDashboardsForProfile/Karmayogi': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/dashboard/analytics/getChartV2/Karmayogi': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/wat/dashboard/getDashboardConfig/Karmayogi/overview': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/upload/action/content/v3/upload/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.EDITOR, + ROLE.PUBLIC, ], }, '/proxies/v8/v1/content/retire': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.EDITOR, + ROLE.PUBLIC, ], }, '/proxies/v8/action/content/v3/updateReviewStatus/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_REVIEWER, - ROLE.CONTENT_PUBLISHER, + ROLE.PUBLIC, ], }, '/proxies/v8/action/content/v3/review/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.CONTENT_REVIEWER, - ROLE.CONTENT_PUBLISHER, + ROLE.PUBLIC, ], }, '/proxies/v8/action/content/v3/publish/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_PUBLISHER, + ROLE.PUBLIC, ], }, '/proxies/v8/action/content/v3/update/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.EDITOR, - ROLE.CONTENT_REVIEWER, - ROLE.CONTENT_PUBLISHER, + ROLE.PUBLIC, ], }, '/proxies/v8/data/v1/system/settings/get/orgTypeList': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/org/v1/search': { @@ -354,8 +332,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/user/v1/search': { @@ -369,41 +346,35 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/org/v1/update': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/user/v1/block': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/user/v1/unblock': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/data/v1/system/settings/get/orgTypeConfig': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/discussion/tags': { @@ -417,23 +388,21 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/learnervm/private/content/v3/publish/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_PUBLISHER, + ROLE.PUBLIC, ], }, '/proxies/v8/learnervm/private/content/v3/review/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_REVIEWER, + ROLE.PUBLIC, ], }, '/proxies/v8/discussion/v2/topics': { @@ -503,18 +472,14 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/user/private/v1/migrate': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/user/private/v1/assign/role/userrole': { @@ -556,16 +521,14 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.EDITOR, + ROLE.PUBLIC, ], }, '/proxies/v8/private/content/v3/update/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.EDITOR, + ROLE.PUBLIC, ], }, '/proxies/v8/notifyContentState': { @@ -600,8 +563,6 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.EDITOR, ROLE.PUBLIC, ], }, @@ -658,23 +619,21 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/portal/spv/department': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/portal/spv/deptAction/': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/user/roles/getUsersV2': { @@ -688,22 +647,21 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/portal/mdo/mydepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/user/profileDetails/createUser': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/user/profileRegistry/getUserRegistryByUser/:id': { @@ -731,19 +689,14 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workflowhandler/historyByApplicationId/:applicationId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/user/autocomplete/:query': { @@ -757,7 +710,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/user/preference': { @@ -771,40 +724,28 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workallocation/getWorkOrders': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workallocation/add/workorder': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workallocation/getWorkOrderById/:workOrderId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], SCOPE_CHECK: [ ROLE.MDO_ADMIN, @@ -849,47 +790,35 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workallocation/getWorkAllocationById/:workAllocationId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workallocation/update/workorder': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workallocation/getWOPdf/:workOrderId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/portal/cbp/mydepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/user/mandatoryContent/checkStatus': { @@ -952,31 +881,28 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/portal/cbc/department/:deptId/': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/portal/spv/department/:deptId/': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/scroing/calculate': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.EDITOR, - ROLE.CONTENT_PUBLISHER, - ROLE.CONTENT_REVIEWER, + ROLE.PUBLIC, ], }, // '/protected/v8/connections/connections/recommended': { @@ -1046,28 +972,20 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.EDITOR, - ROLE.CONTENT_PUBLISHER, - ROLE.CONTENT_REVIEWER, + ROLE.PUBLIC, ], }, '/protected/v8/profanity/getPdfProfanityForContent/:contentId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.CONTENT_REVIEWER, - ROLE.CONTENT_PUBLISHER, - ROLE.EDITOR, + ROLE.PUBLIC, ], }, '/protected/v8/catalog': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.EDITOR, ROLE.PUBLIC, ], }, @@ -1075,27 +993,21 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.CONTENT_REVIEWER, - ROLE.EDITOR, - ROLE.CONTENT_PUBLISHER, + ROLE.PUBLIC, ], }, '/protected/v8/portal/mdo/deptAction/userrole': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workallocation/v2/update': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/user/profileDetails/updateUser': { @@ -1130,10 +1042,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/user/details/detailV1': { @@ -1189,50 +1098,35 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workflowhandler/historyByApplicationIdAndWfId/:applicationId/:wfId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workflowhandler/workflowProcess/:wfId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workflowhandler/updateUserProfileWf': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workflowhandler/userWfSearch': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/user/profileRegistry/getUserRegistryById': { @@ -1253,10 +1147,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/reset': { @@ -1345,7 +1236,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/authApi/batch/:key': { @@ -1371,39 +1262,37 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/staff/position/:orgId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/budget/scheme': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/budget/scheme/:orgId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/budget/scheme/:orgId/:budgetYear': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/orghistory/:orgId/:key': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/discussion/user/uid/:uid': { @@ -1430,13 +1319,6 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/proxies/v8/action/framework/v3/read/:id': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, '/protected/v8/cohorts/course/batch/cert/issue': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1444,13 +1326,6 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/proxies/v8/action/framework/v3/category/master/create': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, '/protected/v8/cohorts/course/batch/cert/template/add': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1458,62 +1333,6 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/proxies/v8/action/framework/v3/category/master/search': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, - '/proxies/v8/action/framework/v3/category/master/read/:id': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, - '/proxies/v8/action/framework/v3/category/create': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, - '/proxies/v8/action/framework/v3/category/read/:id': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, - '/proxies/v8/action/framework/v3/category/retire/:id': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, - '/proxies/v8/action/framework/v3/term/create': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, - '/proxies/v8/action/content/v3/upload/:do_id': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, - '/proxies/v8/learner/course/v1/batch/list': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, }, URL_PATTERN: [ @@ -1703,16 +1522,6 @@ export const API_LIST = { '/proxies/v8/budget/scheme/:orgId/:budgetYear', '/proxies/v8/orghistory/:orgId/:key', '/proxies/v8/discussion/user/uid/:uid', - '/proxies/v8/discussion/user/:username/posts', - '/proxies/v8/action/framework/v3/read/:id', - '/proxies/v8/action/framework/v3/category/master/create', - '/proxies/v8/action/framework/v3/category/master/search', - '/proxies/v8/action/framework/v3/category/master/read/:id', - '/proxies/v8/action/framework/v3/category/create', - '/proxies/v8/action/framework/v3/category/read/:id', - '/proxies/v8/action/framework/v3/category/retire/:id', - '/proxies/v8/action/framework/v3/term/create', - '/proxies/v8/action/content/v3/upload/:do_id', - '/proxies/v8/learner/course/v1/batch/list', + '/proxies/v8/discussion/user/:username/posts' ], } From 40af766223e07ee69ed94b5662e073517331ef41 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Fri, 19 Nov 2021 18:11:23 +0530 Subject: [PATCH 194/856] token change in env.ts --- src/utils/env.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/env.ts b/src/utils/env.ts index 5f8a55e4..d3b5a3fb 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -154,7 +154,7 @@ export const CONSTANTS = { CONTENT_SERVICE_API_BASE: env.CONTENT_SERVICE_API_BASE || 'http://content-service:9000', VM_LEARNING_SERVICE_URL: env.VM_LEARNING_SERVICE_URL, // tslint:disable-next-line: max-line-length - CERT_AUTH_TOKEN: 'bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJVMkpUdlpERFY4eG83ZmtfNHd1Yy1kNVJmNjRPTG1oemlRRUhjR25Vc2hNIn0.eyJqdGkiOiIyM2YwMzUyOC1lNTUwLTQ2OGQtOWRkYy0yZDMwNDJkNjliMjQiLCJleHAiOjE2MzczOTMzMzksIm5iZiI6MCwiaWF0IjoxNjM3MzA2OTM5LCJpc3MiOiJodHRwczovL2lnb3QtZGV2LmluL2F1dGgvcmVhbG1zL3N1bmJpcmQiLCJhdWQiOiJhZG1pbi1jbGkiLCJzdWIiOiJmOjkyM2JkYzE4LTUyMGQtNDhkNC1hODRlLTNjZGUxZTY1NWViZDo3NTg1NGI0ZC01ZWMyLTRlYzMtYTE4ZC05NTIwMzAzODQzNjEiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhZG1pbi1jbGkiLCJhdXRoX3RpbWUiOjAsInNlc3Npb25fc3RhdGUiOiI4MGNiOTg4OC00MzMzLTQ4YzMtYmIyZS00ZTA5MjUyOWU1OTYiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnt9LCJuYW1lIjoiYmVzY29tIGNvbnRlbnQiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJiZXNjb21jb250ZW50X3EzZjMiLCJnaXZlbl9uYW1lIjoiYmVzY29tIiwiZmFtaWx5X25hbWUiOiJjb250ZW50IiwiZW1haWwiOiJiZSoqKioqKioqKioqKkB5b3BtYWlsLmNvbSJ9.jwBhB-I2zZKArkIv-NdDfgNoYLdofaq-zR8sfSR5eMW25uHNcWQnBHIdA7CQp02bP_2RP2Zw1PF4S2PrJM8r5OVyRjEOx-R1LPun8I7BtQIb4k5vV9DupZack5V8HB800x9kbCCgZxxBWAT4ud7UmmQkzX-gIWw4XPWuxY4mTy7BhVhtW88IkfDB7GPxmuUGBOUJQ3ZOehDg8sWHGpDfF8ZEf3M_XHeeEap23iviFGlcpcb4uOi9SDsTpU9MaeoN4skC6fvr9SUD3WJo276ateGnJElyflckLQhhvX23rROtCif4Ouip-o3vtjGblaaTdcNbPw10z-74wNIraM7yxA', + CERT_AUTH_TOKEN: 'bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJRekw4VVA1dUtqUFdaZVpMd1ZtTFJvNHdqWTg2a2FrcSJ9.TPjV0xLacSbp3FbJ7XeqHoKFN35Rl4YHx3DZNN9pm0o', } From 38eaf635d39eb4db3228b92030efd0eca89cf53d Mon Sep 17 00:00:00 2001 From: SuvajitTarento <72616697+SuvajitTarento@users.noreply.github.com> Date: Mon, 22 Nov 2021 14:20:00 +0530 Subject: [PATCH 195/856] Cbrelease 3.0.1 rc checkup (#72) * Added Framework related APIs * Changes for bulk upload * lint fix Co-authored-by: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> --- src/models/user.model.ts | 1 + src/protectedApi_v8/cohorts.ts | 2 +- src/protectedApi_v8/user/profile-details.ts | 87 +++++++++++---------- src/utils/whitelistApis.ts | 66 +++++++++++++++- 4 files changed, 113 insertions(+), 43 deletions(-) diff --git a/src/models/user.model.ts b/src/models/user.model.ts index 96e4a75e..966bd3d3 100644 --- a/src/models/user.model.ts +++ b/src/models/user.model.ts @@ -151,4 +151,5 @@ export interface ISunbirdbUserResponse { firstName: string lastName: string userId: string + userOrgId: string } diff --git a/src/protectedApi_v8/cohorts.ts b/src/protectedApi_v8/cohorts.ts index f58b4878..07e4f629 100644 --- a/src/protectedApi_v8/cohorts.ts +++ b/src/protectedApi_v8/cohorts.ts @@ -298,7 +298,7 @@ function getUsers(userprofile: IUserProfile): ICohortsUser { if (userprofile.professionalDetails[0].designation !== undefined) { designationValue = userprofile.professionalDetails[0].designation } else { - designationValue = userprofile.professionalDetails[0].designationOther === undefined ? '' : + designationValue = userprofile.professionalDetails[0].designationOther === undefined ? '' : userprofile.professionalDetails[0].designationOther } } diff --git a/src/protectedApi_v8/user/profile-details.ts b/src/protectedApi_v8/user/profile-details.ts index 4168083d..c865b998 100644 --- a/src/protectedApi_v8/user/profile-details.ts +++ b/src/protectedApi_v8/user/profile-details.ts @@ -225,6 +225,8 @@ profileDeatailsApi.post('/createUser', async (req, res) => { const sbemailVerified_ = true const sbfirstName_ = req.body.personalDetails.firstName const sblastName_ = req.body.personalDetails.lastName + // For controlling the sent email function + const isEmailRequired = (req.body.personalDetails.isEmailRequired) ? req.body.personalDetails.isEmailRequired : true const searchresponse = await axios({ ...axiosRequestConfig, @@ -293,6 +295,7 @@ profileDeatailsApi.post('/createUser', async (req, res) => { return } + const sbUserOrgId = sbUserReadResponse.data.result.response.rootOrgId const sbProfileUpdateReq = { profileDetails: { employmentDetails: { @@ -320,62 +323,64 @@ profileDeatailsApi.post('/createUser', async (req, res) => { res.status(400).send(failedToUpdateUser) return } - - const passwordResetRequest = { - key: 'email', - type: 'email', - userId: sbUserId, - } - - logInfo('Sending Password reset request -> ' + passwordResetRequest) - const passwordResetResponse = await axios({ - ...axiosRequestConfig, - data: { request: passwordResetRequest }, - headers: { - Authorization: CONSTANTS.SB_API_KEY, - }, - method: 'POST', - url: API_END_POINTS.kongUserResetPassword, - }) - logInfo('Received response from password reset -> ' + passwordResetResponse) - - if (passwordResetResponse.data.params.status === 'success') { - const welcomeMailRequest = { - allowedLoging: 'You can use your email to Login', - body: 'Hello', - emailTemplateType: 'iGotWelcome', - firstName: sbUserProfile.firstName, - link: passwordResetResponse.data.result.link, - mode: 'email', - orgName: sbChannel, - recipientEmails: [ sbemail_ ], - setPasswordLink: true, - subject: 'Welcome Email', - welcomeMessage: 'Hello', + if (isEmailRequired) { + const passwordResetRequest = { + key: 'email', + type: 'email', + userId: sbUserId, } - const welcomeMailResponse = await axios({ + logInfo('Sending Password reset request -> ' + passwordResetRequest) + const passwordResetResponse = await axios({ ...axiosRequestConfig, - data: { request: welcomeMailRequest }, + data: { request: passwordResetRequest }, headers: { Authorization: CONSTANTS.SB_API_KEY, }, method: 'POST', - url: API_END_POINTS.kongSendWelcomeEmail, + url: API_END_POINTS.kongUserResetPassword, }) - - if (welcomeMailResponse.data.params.status !== 'success') { - res.status(500).send('Failed to send Welcome Email.') + logInfo('Received response from password reset -> ' + passwordResetResponse) + + if (passwordResetResponse.data.params.status === 'success') { + const welcomeMailRequest = { + allowedLoging: 'You can use your email to Login', + body: 'Hello', + emailTemplateType: 'iGotWelcome', + firstName: sbUserProfile.firstName, + link: passwordResetResponse.data.result.link, + mode: 'email', + orgName: sbChannel, + recipientEmails: [ sbemail_ ], + setPasswordLink: true, + subject: 'Welcome Email', + welcomeMessage: 'Hello', + } + + const welcomeMailResponse = await axios({ + ...axiosRequestConfig, + data: { request: welcomeMailRequest }, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + }, + method: 'POST', + url: API_END_POINTS.kongSendWelcomeEmail, + }) + + if (welcomeMailResponse.data.params.status !== 'success') { + res.status(500).send('Failed to send Welcome Email.') + return + } + } else { + res.status(500).send('Failed to reset the password for user.') return } - } else { - res.status(500).send('Failed to reset the password for user.') - return } const sbUserProfileResponse: Partial = { email: sbemail_, firstName: sbfirstName_, lastName: sblastName_, userId: sbUserId, + userOrgId: sbUserOrgId, } res.send(sbUserProfileResponse) } diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index aebc38f0..ca28d2fb 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1295,6 +1295,27 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/action/framework/v3/read/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/action/framework/v3/category/master/create': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/action/framework/v3/category/master/search': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/discussion/user/uid/:uid': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1302,6 +1323,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/action/framework/v3/category/master/read/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/discussion/user/:username/posts': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1309,6 +1337,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/action/framework/v3/category/create': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, // '/protected/v8/cohorts/course/batch/cert/download/', // '/protected/v8/cohorts/course/batch/cert/issue', // '/protected/v8/cohorts/course/batch/cert/template/add', @@ -1319,6 +1354,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/action/framework/v3/category/read/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/cohorts/course/batch/cert/issue': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1326,6 +1368,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/action/framework/v3/category/retire/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/cohorts/course/batch/cert/template/add': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1333,6 +1382,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/action/framework/v3/term/create': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -1522,6 +1578,14 @@ export const API_LIST = { '/proxies/v8/budget/scheme/:orgId/:budgetYear', '/proxies/v8/orghistory/:orgId/:key', '/proxies/v8/discussion/user/uid/:uid', - '/proxies/v8/discussion/user/:username/posts' + '/proxies/v8/discussion/user/:username/posts', + '/proxies/v8/action/framework/v3/read/:id', + '/proxies/v8/action/framework/v3/category/master/create', + '/proxies/v8/action/framework/v3/category/master/search', + '/proxies/v8/action/framework/v3/category/master/read/:id', + '/proxies/v8/action/framework/v3/category/create', + '/proxies/v8/action/framework/v3/category/read/:id', + '/proxies/v8/action/framework/v3/category/retire/:id', + '/proxies/v8/action/framework/v3/term/create', ], } From ec9bec769d5416ee184697adee57e3dcd640565b Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Mon, 22 Nov 2021 17:56:57 +0530 Subject: [PATCH 196/856] Fix merge issues --- src/utils/whitelistApis.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 24366bd3..ea3d0a9f 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1490,6 +1490,27 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/cohorts/course/batch/cert/download/:certId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/cohorts/course/batch/cert/issue': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/protected/v8/cohorts/course/batch/cert/template/add': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -1687,5 +1708,8 @@ export const API_LIST = { '/proxies/v8/action/framework/v3/term/create', '/proxies/v8/action/content/v3/upload/:do_id', '/proxies/v8/learner/course/v1/batch/list', + '/protected/v8/cohorts/course/batch/cert/download/:certId', + '/protected/v8/cohorts/course/batch/cert/issue', + '/protected/v8/cohorts/course/batch/cert/template/add', ], } From 059c846079e69fa1a47a03f2123b7530153bab20 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Mon, 22 Nov 2021 18:05:16 +0530 Subject: [PATCH 197/856] FIx duplicates --- src/utils/whitelistApis.ts | 291 +++++++++++-------------------------- 1 file changed, 83 insertions(+), 208 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index ea3d0a9f..a27d3beb 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -80,14 +80,14 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/event/v4/create': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/user/v1/read/:id': { @@ -178,8 +178,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_REVIEWER, - ROLE.CONTENT_PUBLISHER, + ROLE.PUBLIC, ], }, '/proxies/v8/discussion/forum/v2/read': { @@ -228,119 +227,98 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_REVIEWER, + ROLE.PUBLIC, ], }, '/proxies/v8/action/content/v3/hierarchy/update': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.EDITOR, - ROLE.CONTENT_PUBLISHER, - ROLE.CONTENT_REVIEWER, + ROLE.PUBLIC, ], }, '/proxies/v8/action/content/v3/create': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, + ROLE.PUBLIC, ], }, '/proxies/v8/dashboard/analytics/getDashboardConfig/Karmayogi/:comp': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/dashboard/analytics/getDashboardsForProfile/Karmayogi': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/dashboard/analytics/getChartV2/Karmayogi': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/wat/dashboard/getDashboardConfig/Karmayogi/overview': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/upload/action/content/v3/upload/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.EDITOR, + ROLE.PUBLIC, ], }, '/proxies/v8/v1/content/retire': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.EDITOR, + ROLE.PUBLIC, ], }, '/proxies/v8/action/content/v3/updateReviewStatus/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_REVIEWER, - ROLE.CONTENT_PUBLISHER, + ROLE.PUBLIC, ], }, '/proxies/v8/action/content/v3/review/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.CONTENT_REVIEWER, - ROLE.CONTENT_PUBLISHER, + ROLE.PUBLIC, ], }, '/proxies/v8/action/content/v3/publish/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_PUBLISHER, + ROLE.PUBLIC, ], }, '/proxies/v8/action/content/v3/update/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.EDITOR, - ROLE.CONTENT_REVIEWER, - ROLE.CONTENT_PUBLISHER, + ROLE.PUBLIC, ], }, '/proxies/v8/data/v1/system/settings/get/orgTypeList': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/org/v1/search': { @@ -354,8 +332,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/user/v1/search': { @@ -369,41 +346,35 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/org/v1/update': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/user/v1/block': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/user/v1/unblock': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/data/v1/system/settings/get/orgTypeConfig': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/discussion/tags': { @@ -417,23 +388,21 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/learnervm/private/content/v3/publish/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_PUBLISHER, + ROLE.PUBLIC, ], }, '/proxies/v8/learnervm/private/content/v3/review/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_REVIEWER, + ROLE.PUBLIC, ], }, '/proxies/v8/discussion/v2/topics': { @@ -503,18 +472,14 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/user/private/v1/migrate': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/user/private/v1/assign/role/userrole': { @@ -556,16 +521,14 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.EDITOR, + ROLE.PUBLIC, ], }, '/proxies/v8/private/content/v3/update/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.EDITOR, + ROLE.PUBLIC, ], }, '/proxies/v8/notifyContentState': { @@ -600,8 +563,6 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.EDITOR, ROLE.PUBLIC, ], }, @@ -658,23 +619,21 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/portal/spv/department': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/portal/spv/deptAction/': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/user/roles/getUsersV2': { @@ -688,22 +647,21 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/portal/mdo/mydepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/user/profileDetails/createUser': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/user/profileRegistry/getUserRegistryByUser/:id': { @@ -731,19 +689,14 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workflowhandler/historyByApplicationId/:applicationId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/user/autocomplete/:query': { @@ -757,7 +710,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/user/preference': { @@ -771,40 +724,28 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workallocation/getWorkOrders': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workallocation/add/workorder': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workallocation/getWorkOrderById/:workOrderId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], SCOPE_CHECK: [ ROLE.MDO_ADMIN, @@ -849,47 +790,35 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workallocation/getWorkAllocationById/:workAllocationId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workallocation/update/workorder': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workallocation/getWOPdf/:workOrderId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/portal/cbp/mydepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/user/mandatoryContent/checkStatus': { @@ -952,31 +881,28 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/portal/cbc/department/:deptId/': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CBC_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/portal/spv/department/:deptId/': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/scroing/calculate': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.EDITOR, - ROLE.CONTENT_PUBLISHER, - ROLE.CONTENT_REVIEWER, + ROLE.PUBLIC, ], }, // '/protected/v8/connections/connections/recommended': { @@ -1046,28 +972,20 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.EDITOR, - ROLE.CONTENT_PUBLISHER, - ROLE.CONTENT_REVIEWER, + ROLE.PUBLIC, ], }, '/protected/v8/profanity/getPdfProfanityForContent/:contentId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.CONTENT_REVIEWER, - ROLE.CONTENT_PUBLISHER, - ROLE.EDITOR, + ROLE.PUBLIC, ], }, '/protected/v8/catalog': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.EDITOR, ROLE.PUBLIC, ], }, @@ -1075,27 +993,21 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.CONTENT_CREATOR, - ROLE.CONTENT_REVIEWER, - ROLE.EDITOR, - ROLE.CONTENT_PUBLISHER, + ROLE.PUBLIC, ], }, '/protected/v8/portal/mdo/deptAction/userrole': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workallocation/v2/update': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/user/profileDetails/updateUser': { @@ -1130,10 +1042,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/user/details/detailV1': { @@ -1189,50 +1098,35 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workflowhandler/historyByApplicationIdAndWfId/:applicationId/:wfId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workflowhandler/workflowProcess/:wfId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workflowhandler/updateUserProfileWf': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/workflowhandler/userWfSearch': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/user/profileRegistry/getUserRegistryById': { @@ -1253,10 +1147,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/reset': { @@ -1345,7 +1236,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/authApi/batch/:key': { @@ -1371,49 +1262,33 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/staff/position/:orgId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/budget/scheme': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/budget/scheme/:orgId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/budget/scheme/:orgId/:budgetYear': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ], - }, '/proxies/v8/orghistory/:orgId/:key': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.CBC_ADMIN, - ROLE.MDO_ADMIN, - ROLE.SPV_ADMIN, - ], - }, - '/proxies/v8/discussion/user/uid/:uid': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], - }, - '/proxies/v8/discussion/user/:username/posts': { + }, '/proxies/v8/orghistory/:orgId/:key': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -1441,70 +1316,70 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/proxies/v8/action/framework/v3/category/master/read/:id': { + '/proxies/v8/discussion/user/uid/:uid': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/proxies/v8/action/framework/v3/category/create': { + '/proxies/v8/action/framework/v3/category/master/read/:id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/proxies/v8/action/framework/v3/category/read/:id': { + '/proxies/v8/discussion/user/:username/posts': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/proxies/v8/action/framework/v3/category/retire/:id': { + '/proxies/v8/action/framework/v3/category/create': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/proxies/v8/action/framework/v3/term/create': { + '/protected/v8/cohorts/course/batch/cert/download/:certId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/proxies/v8/action/content/v3/upload/:do_id': { + '/proxies/v8/action/framework/v3/category/read/:id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/proxies/v8/learner/course/v1/batch/list': { + '/protected/v8/cohorts/course/batch/cert/issue': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/protected/v8/cohorts/course/batch/cert/download/:certId': { + '/proxies/v8/action/framework/v3/category/retire/:id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/protected/v8/cohorts/course/batch/cert/issue': { + '/protected/v8/cohorts/course/batch/cert/template/add': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/protected/v8/cohorts/course/batch/cert/template/add': { + '/proxies/v8/action/framework/v3/term/create': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -1689,6 +1564,9 @@ export const API_LIST = { '/protected/v8/connections/v2/connections/suggests', '/protected/v8/connections/v2/update/connection', '/protected/v8/user/profileDetails/createUserWithoutInvitationEmail', + '/protected/v8/cohorts/course/batch/cert/download/:certId', + '/protected/v8/cohorts/course/batch/cert/issue', + '/protected/v8/cohorts/course/batch/cert/template/add', '/proxies/v8/searchBy/:key', '/proxies/v8/staff/position', '/proxies/v8/staff/position/:orgId', @@ -1708,8 +1586,5 @@ export const API_LIST = { '/proxies/v8/action/framework/v3/term/create', '/proxies/v8/action/content/v3/upload/:do_id', '/proxies/v8/learner/course/v1/batch/list', - '/protected/v8/cohorts/course/batch/cert/download/:certId', - '/protected/v8/cohorts/course/batch/cert/issue', - '/protected/v8/cohorts/course/batch/cert/template/add', ], } From 4b0044c7484999b968e9f67bc48721cafac6b619 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Mon, 22 Nov 2021 18:13:21 +0530 Subject: [PATCH 198/856] Fix merge issues --- src/utils/whitelistApis.ts | 295 ++++++++++++++++++++++++++----------- 1 file changed, 211 insertions(+), 84 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 6fe9d2c6..f2fdb739 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -80,14 +80,14 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/event/v4/create': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/user/v1/read/:id': { @@ -178,7 +178,8 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_REVIEWER, + ROLE.CONTENT_PUBLISHER, ], }, '/proxies/v8/discussion/forum/v2/read': { @@ -227,98 +228,119 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_REVIEWER, ], }, '/proxies/v8/action/content/v3/hierarchy/update': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, + ROLE.CONTENT_PUBLISHER, + ROLE.CONTENT_REVIEWER, ], }, '/proxies/v8/action/content/v3/create': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, ], }, '/proxies/v8/dashboard/analytics/getDashboardConfig/Karmayogi/:comp': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/dashboard/analytics/getDashboardsForProfile/Karmayogi': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/dashboard/analytics/getChartV2/Karmayogi': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/wat/dashboard/getDashboardConfig/Karmayogi/overview': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/upload/action/content/v3/upload/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, ], }, '/proxies/v8/v1/content/retire': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, ], }, '/proxies/v8/action/content/v3/updateReviewStatus/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_REVIEWER, + ROLE.CONTENT_PUBLISHER, ], }, '/proxies/v8/action/content/v3/review/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.CONTENT_REVIEWER, + ROLE.CONTENT_PUBLISHER, ], }, '/proxies/v8/action/content/v3/publish/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_PUBLISHER, ], }, '/proxies/v8/action/content/v3/update/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, + ROLE.CONTENT_REVIEWER, + ROLE.CONTENT_PUBLISHER, ], }, '/proxies/v8/data/v1/system/settings/get/orgTypeList': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/org/v1/search': { @@ -332,7 +354,8 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/user/v1/search': { @@ -346,35 +369,41 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/org/v1/update': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/user/v1/block': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/user/v1/unblock': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/data/v1/system/settings/get/orgTypeConfig': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/discussion/tags': { @@ -388,21 +417,23 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/learnervm/private/content/v3/publish/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_PUBLISHER, ], }, '/proxies/v8/learnervm/private/content/v3/review/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_REVIEWER, ], }, '/proxies/v8/discussion/v2/topics': { @@ -472,14 +503,18 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/user/private/v1/migrate': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/user/private/v1/assign/role/userrole': { @@ -521,14 +556,16 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, ], }, '/proxies/v8/private/content/v3/update/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, ], }, '/proxies/v8/notifyContentState': { @@ -563,6 +600,8 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, ROLE.PUBLIC, ], }, @@ -619,21 +658,23 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, + ROLE.MDO_ADMIN, ], }, '/protected/v8/portal/spv/department': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.SPV_ADMIN, ], }, '/protected/v8/portal/spv/deptAction/': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.SPV_ADMIN, ], }, '/protected/v8/user/roles/getUsersV2': { @@ -647,21 +688,22 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.SPV_ADMIN, ], }, '/protected/v8/portal/mdo/mydepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/protected/v8/user/profileDetails/createUser': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/user/profileRegistry/getUserRegistryByUser/:id': { @@ -689,14 +731,19 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workflowhandler/historyByApplicationId/:applicationId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/user/autocomplete/:query': { @@ -710,7 +757,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.SPV_ADMIN, ], }, '/protected/v8/user/preference': { @@ -724,28 +771,40 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workallocation/getWorkOrders': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workallocation/add/workorder': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workallocation/getWorkOrderById/:workOrderId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], SCOPE_CHECK: [ ROLE.MDO_ADMIN, @@ -790,35 +849,47 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workallocation/getWorkAllocationById/:workAllocationId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workallocation/update/workorder': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workallocation/getWOPdf/:workOrderId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/portal/cbp/mydepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, ], }, '/protected/v8/user/mandatoryContent/checkStatus': { @@ -881,28 +952,31 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, ], }, '/protected/v8/portal/cbc/department/:deptId/': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CBC_ADMIN, ], }, '/protected/v8/portal/spv/department/:deptId/': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.SPV_ADMIN, ], }, '/protected/v8/scroing/calculate': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, + ROLE.CONTENT_PUBLISHER, + ROLE.CONTENT_REVIEWER, ], }, // '/protected/v8/connections/connections/recommended': { @@ -972,20 +1046,28 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, + ROLE.CONTENT_PUBLISHER, + ROLE.CONTENT_REVIEWER, ], }, '/protected/v8/profanity/getPdfProfanityForContent/:contentId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.CONTENT_REVIEWER, + ROLE.CONTENT_PUBLISHER, + ROLE.EDITOR, ], }, '/protected/v8/catalog': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ + ROLE.CONTENT_CREATOR, + ROLE.EDITOR, ROLE.PUBLIC, ], }, @@ -993,21 +1075,27 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.CONTENT_CREATOR, + ROLE.CONTENT_REVIEWER, + ROLE.EDITOR, + ROLE.CONTENT_PUBLISHER, ], }, '/protected/v8/portal/mdo/deptAction/userrole': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/protected/v8/workallocation/v2/update': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/user/profileDetails/updateUser': { @@ -1042,7 +1130,10 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/user/details/detailV1': { @@ -1098,35 +1189,50 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workflowhandler/historyByApplicationIdAndWfId/:applicationId/:wfId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workflowhandler/workflowProcess/:wfId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workflowhandler/updateUserProfileWf': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/workflowhandler/userWfSearch': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/protected/v8/user/profileRegistry/getUserRegistryById': { @@ -1147,7 +1253,10 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.WAT_MEMBER, + ROLE.CBC_ADMIN, + ROLE.SPV_ADMIN, ], }, '/reset': { @@ -1236,7 +1345,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/authApi/batch/:key': { @@ -1262,33 +1371,49 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/staff/position/:orgId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/budget/scheme': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/budget/scheme/:orgId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/budget/scheme/:orgId/:budgetYear': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/orghistory/:orgId/:key': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.CBC_ADMIN, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, + ], + }, + '/proxies/v8/discussion/user/uid/:uid': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/discussion/user/:username/posts': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -1316,70 +1441,70 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/proxies/v8/discussion/user/uid/:uid': { + '/proxies/v8/action/framework/v3/category/master/read/:id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/proxies/v8/action/framework/v3/category/master/read/:id': { + '/proxies/v8/action/framework/v3/category/create': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/proxies/v8/discussion/user/:username/posts': { + '/proxies/v8/action/framework/v3/category/read/:id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/proxies/v8/action/framework/v3/category/create': { + '/proxies/v8/action/framework/v3/category/retire/:id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/protected/v8/cohorts/course/batch/cert/download/:certId': { + '/proxies/v8/action/framework/v3/term/create': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/proxies/v8/action/framework/v3/category/read/:id': { + '/proxies/v8/action/content/v3/upload/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/protected/v8/cohorts/course/batch/cert/issue': { + '/proxies/v8/learner/course/v1/batch/list': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/proxies/v8/action/framework/v3/category/retire/:id': { + '/protected/v8/cohorts/course/batch/cert/download/:certId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/protected/v8/cohorts/course/batch/cert/template/add': { + '/protected/v8/cohorts/course/batch/cert/issue': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/proxies/v8/action/framework/v3/term/create': { + '/protected/v8/cohorts/course/batch/cert/template/add': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -1564,9 +1689,6 @@ export const API_LIST = { '/protected/v8/connections/v2/connections/suggests', '/protected/v8/connections/v2/update/connection', '/protected/v8/user/profileDetails/createUserWithoutInvitationEmail', - '/protected/v8/cohorts/course/batch/cert/download/:certId', - '/protected/v8/cohorts/course/batch/cert/issue', - '/protected/v8/cohorts/course/batch/cert/template/add', '/proxies/v8/searchBy/:key', '/proxies/v8/staff/position', '/proxies/v8/staff/position/:orgId', @@ -1584,5 +1706,10 @@ export const API_LIST = { '/proxies/v8/action/framework/v3/category/read/:id', '/proxies/v8/action/framework/v3/category/retire/:id', '/proxies/v8/action/framework/v3/term/create', + '/proxies/v8/action/content/v3/upload/:do_id', + '/proxies/v8/learner/course/v1/batch/list', + '/protected/v8/cohorts/course/batch/cert/download/:certId', + '/protected/v8/cohorts/course/batch/cert/issue', + '/protected/v8/cohorts/course/batch/cert/template/add', ], -} +} \ No newline at end of file From ea1b3415e19a7baa2b31ca550b89659a7b62b0db Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Tue, 23 Nov 2021 19:37:14 +0530 Subject: [PATCH 199/856] Changes for bulk upload nic --- src/protectedApi_v8/user/profile-details.ts | 7 +++++++ src/utils/whitelistApis.ts | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/protectedApi_v8/user/profile-details.ts b/src/protectedApi_v8/user/profile-details.ts index c865b998..e6f2b00f 100644 --- a/src/protectedApi_v8/user/profile-details.ts +++ b/src/protectedApi_v8/user/profile-details.ts @@ -227,6 +227,7 @@ profileDeatailsApi.post('/createUser', async (req, res) => { const sblastName_ = req.body.personalDetails.lastName // For controlling the sent email function const isEmailRequired = (req.body.personalDetails.isEmailRequired) ? req.body.personalDetails.isEmailRequired : true + const sbDesignation = (req.body.personalDetails.designation) ? req.body.personalDetails.designation : '' const searchresponse = await axios({ ...axiosRequestConfig, @@ -295,6 +296,11 @@ profileDeatailsApi.post('/createUser', async (req, res) => { return } + const arrDesignation = [] + const objDesignation = { + designation: (sbDesignation) ? sbDesignation : '', + } + arrDesignation.push(objDesignation) const sbUserOrgId = sbUserReadResponse.data.result.response.rootOrgId const sbProfileUpdateReq = { profileDetails: { @@ -306,6 +312,7 @@ profileDeatailsApi.post('/createUser', async (req, res) => { primaryEmail: sbemail_, surname: sblastName_, }, + professionalDetails: arrDesignation, }, userId: sbUserId, } diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index f2fdb739..ea3d0a9f 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1712,4 +1712,4 @@ export const API_LIST = { '/protected/v8/cohorts/course/batch/cert/issue', '/protected/v8/cohorts/course/batch/cert/template/add', ], -} \ No newline at end of file +} From 0848c2b710529be0a659a4a521f8615fd6e8ee53 Mon Sep 17 00:00:00 2001 From: SuvajitTarento <72616697+SuvajitTarento@users.noreply.github.com> Date: Wed, 24 Nov 2021 14:07:29 +0530 Subject: [PATCH 200/856] Cbrelease 3.0.1 task56069 (#76) * init commit * Changes for profile * Changes for nodebb user creation --- src/protectedApi_v8/user/profile-details.ts | 27 ++++++++++++ src/utils/permissionHelper.ts | 48 +++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/src/protectedApi_v8/user/profile-details.ts b/src/protectedApi_v8/user/profile-details.ts index e6f2b00f..33caa706 100644 --- a/src/protectedApi_v8/user/profile-details.ts +++ b/src/protectedApi_v8/user/profile-details.ts @@ -15,6 +15,8 @@ const API_END_POINTS = { createOSUserRegistry: (userId: string) => `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/create/profile?userId=${userId}`, createSb: `${CONSTANTS.KONG_API_BASE}/user/v3/create`, createUserRegistry: `${CONSTANTS.USER_PROFILE_API_BASE}/public/v8/profileDetails/createUserRegistry`, + // tslint:disable-next-line: object-literal-sort-keys + createNodeBBUser: `${CONSTANTS.KONG_API_BASE}/discussion/user/v1/create`, getMasterLanguages: `${CONSTANTS.USER_PROFILE_API_BASE}/public/v8/profileDetails/getMasterLanguages`, getMasterNationalities: `${CONSTANTS.USER_PROFILE_API_BASE}/public/v8/profileDetails/getMasterNationalities`, getOSUserRegistryById: (userId: string) => `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/search/profile?userId=${userId}`, @@ -291,11 +293,36 @@ profileDeatailsApi.post('/createUser', async (req, res) => { method: 'GET', url: API_END_POINTS.kongUserRead(sbUserId), }) + if (sbUserReadResponse.data.params.status !== 'success') { res.status(500).send(failedToReadUser) return } + // tslint:disable-next-line: no-commented-code + const nodebbPayload = { + username: sbUserReadResponse.data.result.response.userName, + // tslint:disable-next-line: object-literal-sort-keys + identifier: sbUserReadResponse.data.result.response.identifier, + fullname: sbUserReadResponse.data.result.response.firstName + ' ' + sbUserReadResponse.data.result.response.lastName, + } + + await axios({ + ...axiosRequestConfig, + data: { request: nodebbPayload }, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), + }, + method: 'POST', + url: API_END_POINTS.createNodeBBUser, + }) + + // tslint:disable-next-line: no-commented-code + // console.log("UserId", sbUserId) + // console.log("NodeBB", nodeBBResponse.data.result.userId) + const arrDesignation = [] const objDesignation = { designation: (sbDesignation) ? sbDesignation : '', diff --git a/src/utils/permissionHelper.ts b/src/utils/permissionHelper.ts index 952dd976..90003a3a 100644 --- a/src/utils/permissionHelper.ts +++ b/src/utils/permissionHelper.ts @@ -1,7 +1,10 @@ const _ = require('lodash') +import axios from 'axios' import request from 'request' +import { axiosRequestConfig } from '../../src/configs/request.config' import { CONSTANTS } from './env' import { logInfo } from './logger' +import { extractUserToken } from './requestExtract' export const PERMISSION_HELPER = { // tslint:disable-next-line: no-any @@ -12,6 +15,8 @@ export const PERMISSION_HELPER = { if (reqObj.session) { reqObj.session.userId = userData.result.response.id ? userData.result.response.id : userData.result.response.userId reqObj.session.userName = userData.result.response.userName + reqObj.session.firstName = userData.result.response.firstName + reqObj.session.lastName = userData.result.response.lastName reqObj.session.userRoles = userData.result.response.roles reqObj.session.orgs = userData.result.response.organisations reqObj.session.rootOrgId = userData.result.response.rootOrgId @@ -23,12 +28,27 @@ export const PERMISSION_HELPER = { if (error) { callback(error, null) } else { + this.createNodeBBUser(reqObj, callback) callback(null, userData) } }) } }, // tslint:disable-next-line: no-any + setNodeBBUID(reqObj: any, callback: any, body: any) { + // tslint:disable-next-line: no-any + const nodeBBData: any = body + if (reqObj.session) { + reqObj.session.uid = nodeBBData.data.result.userId.uid + } + // tslint:disable-next-line: no-any + reqObj.session.save((error: any) => { + if (error) { + callback(error, null) + } + }) + }, + // tslint:disable-next-line: no-any getCurrentUserRoles(reqObj: any, callback: any) { // console.log('Step 3: Get user roles function') const userId = reqObj.session.userId @@ -50,4 +70,32 @@ export const PERMISSION_HELPER = { } }) }, + // tslint:disable-next-line: no-any + async createNodeBBUser(reqObj: any, callback: any) { + const readUrl = `${CONSTANTS.KONG_API_BASE}/discussion/user/v1/create` + + // tslint:disable-next-line: no-commented-code + const nodebbPayload = { + username: reqObj.session.userName, + // tslint:disable-next-line: object-literal-sort-keys + identifier: reqObj.session.userId, + fullname: reqObj.session.firstName + ' ' + reqObj.session.lastName, + } + + const nodeBBResp = await axios({ + ...axiosRequestConfig, + data: { request: nodebbPayload }, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(reqObj), + }, + method: 'POST', + url: readUrl, + }) + + if (nodeBBResp) { + this.setNodeBBUID(reqObj, callback, nodeBBResp) + } + }, } From e7311d84a27d79f5c863a6dbbe19e2ad9c041404 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Wed, 24 Nov 2021 14:30:52 +0530 Subject: [PATCH 201/856] Lint fix --- src/protectedApi_v8/user/profile-details.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/protectedApi_v8/user/profile-details.ts b/src/protectedApi_v8/user/profile-details.ts index 33caa706..8294b942 100644 --- a/src/protectedApi_v8/user/profile-details.ts +++ b/src/protectedApi_v8/user/profile-details.ts @@ -227,10 +227,8 @@ profileDeatailsApi.post('/createUser', async (req, res) => { const sbemailVerified_ = true const sbfirstName_ = req.body.personalDetails.firstName const sblastName_ = req.body.personalDetails.lastName - // For controlling the sent email function const isEmailRequired = (req.body.personalDetails.isEmailRequired) ? req.body.personalDetails.isEmailRequired : true const sbDesignation = (req.body.personalDetails.designation) ? req.body.personalDetails.designation : '' - const searchresponse = await axios({ ...axiosRequestConfig, data: { request: { query: '', filters: { email: sbemail_.toLowerCase() } } }, @@ -293,12 +291,10 @@ profileDeatailsApi.post('/createUser', async (req, res) => { method: 'GET', url: API_END_POINTS.kongUserRead(sbUserId), }) - if (sbUserReadResponse.data.params.status !== 'success') { res.status(500).send(failedToReadUser) return } - // tslint:disable-next-line: no-commented-code const nodebbPayload = { username: sbUserReadResponse.data.result.response.userName, @@ -306,7 +302,6 @@ profileDeatailsApi.post('/createUser', async (req, res) => { identifier: sbUserReadResponse.data.result.response.identifier, fullname: sbUserReadResponse.data.result.response.firstName + ' ' + sbUserReadResponse.data.result.response.lastName, } - await axios({ ...axiosRequestConfig, data: { request: nodebbPayload }, @@ -319,10 +314,6 @@ profileDeatailsApi.post('/createUser', async (req, res) => { url: API_END_POINTS.createNodeBBUser, }) - // tslint:disable-next-line: no-commented-code - // console.log("UserId", sbUserId) - // console.log("NodeBB", nodeBBResponse.data.result.userId) - const arrDesignation = [] const objDesignation = { designation: (sbDesignation) ? sbDesignation : '', From 648bfef96b6a90fe3912bafed945c1ab58788f53 Mon Sep 17 00:00:00 2001 From: SuvajitTarento Date: Wed, 24 Nov 2021 15:18:23 +0530 Subject: [PATCH 202/856] Path issue fix --- src/utils/permissionHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/permissionHelper.ts b/src/utils/permissionHelper.ts index 90003a3a..cdc7453b 100644 --- a/src/utils/permissionHelper.ts +++ b/src/utils/permissionHelper.ts @@ -1,7 +1,7 @@ const _ = require('lodash') import axios from 'axios' import request from 'request' -import { axiosRequestConfig } from '../../src/configs/request.config' +import { axiosRequestConfig } from '../configs/request.config' import { CONSTANTS } from './env' import { logInfo } from './logger' import { extractUserToken } from './requestExtract' From 10e21597663f13a77f9874d7d6324997f0dc0e99 Mon Sep 17 00:00:00 2001 From: Darshan Nagesh Date: Thu, 25 Nov 2021 11:25:27 +0530 Subject: [PATCH 203/856] Update whitelistApis.ts Adding Dashboard APIs --- src/utils/whitelistApis.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index ea3d0a9f..0963d4ef 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1511,6 +1511,27 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/dashboard/analytics/getDashboardsForProfile/Karmayogi': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/dashboard/analytics/getChartV2/Karmayogi': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/dashboard/analytics/getDashboardConfig/Karmayogi': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -1711,5 +1732,8 @@ export const API_LIST = { '/protected/v8/cohorts/course/batch/cert/download/:certId', '/protected/v8/cohorts/course/batch/cert/issue', '/protected/v8/cohorts/course/batch/cert/template/add', + '/proxies/v8/dashboard/analytics/getDashboardsForProfile/Karmayogi', + '/proxies/v8/dashboard/analytics/getChartV2/Karmayogi', + '/proxies/v8/dashboard/analytics/getDashboardConfig/Karmayogi', ], } From 1d482620c5212027df286c180ca98115687b5a19 Mon Sep 17 00:00:00 2001 From: Darshan Nagesh Date: Thu, 25 Nov 2021 11:36:30 +0530 Subject: [PATCH 204/856] Update whitelistApis.ts Updates on existing API entries --- src/utils/whitelistApis.ts | 40 ++++---------------------------------- 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 0963d4ef..d87f2cc2 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -252,36 +252,28 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/dashboard/analytics/getDashboardsForProfile/Karmayogi': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/dashboard/analytics/getChartV2/Karmayogi': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/wat/dashboard/getDashboardConfig/Karmayogi/overview': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/proxies/v8/upload/action/content/v3/upload/:do_id': { @@ -1511,27 +1503,6 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/proxies/v8/dashboard/analytics/getDashboardsForProfile/Karmayogi': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, - '/proxies/v8/dashboard/analytics/getChartV2/Karmayogi': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, - '/proxies/v8/dashboard/analytics/getDashboardConfig/Karmayogi': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, }, URL_PATTERN: [ @@ -1732,8 +1703,5 @@ export const API_LIST = { '/protected/v8/cohorts/course/batch/cert/download/:certId', '/protected/v8/cohorts/course/batch/cert/issue', '/protected/v8/cohorts/course/batch/cert/template/add', - '/proxies/v8/dashboard/analytics/getDashboardsForProfile/Karmayogi', - '/proxies/v8/dashboard/analytics/getChartV2/Karmayogi', - '/proxies/v8/dashboard/analytics/getDashboardConfig/Karmayogi', ], } From 5dd5f0370784663d671530c4918effc35e41a28d Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 25 Nov 2021 21:12:21 +0530 Subject: [PATCH 205/856] Updated parsing for search user to fix issues --- src/protectedApi_v8/cohorts.ts | 40 ++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/src/protectedApi_v8/cohorts.ts b/src/protectedApi_v8/cohorts.ts index 07e4f629..d91f457c 100644 --- a/src/protectedApi_v8/cohorts.ts +++ b/src/protectedApi_v8/cohorts.ts @@ -294,23 +294,31 @@ cohortsApi.get('/course/getUsersForBatch/:batchId', async (req, res) => { function getUsers(userprofile: IUserProfile): ICohortsUser { let designationValue = '' - if (userprofile.professionalDetails !== undefined && userprofile.professionalDetails.length > 0) { - if (userprofile.professionalDetails[0].designation !== undefined) { - designationValue = userprofile.professionalDetails[0].designation - } else { - designationValue = userprofile.professionalDetails[0].designationOther === undefined ? '' : - userprofile.professionalDetails[0].designationOther + let primaryEmail = '' + let mobileNumber = 0 + if (userprofile.profileDetails !== undefined) { + if (userprofile.profileDetails.professionalDetails !== undefined && userprofile.profileDetails.professionalDetails.length > 0) { + if (userprofile.profileDetails.professionalDetails[0].designation !== undefined) { + designationValue = userprofile.profileDetails.professionalDetails[0].designation + } else { + designationValue = userprofile.profileDetails.professionalDetails[0].designationOther === undefined ? '' : + userprofile.profileDetails.professionalDetails[0].designationOther + } + } + if (userprofile.profileDetails.personalDetails !== undefined) { + primaryEmail = userprofile.profileDetails.personalDetails.primaryEmail + mobileNumber = userprofile.profileDetails.personalDetails.mobile } } return { city: '', - department: userprofile.employmentDetails === undefined ? '' : userprofile.employmentDetails.departmentName, + department: userprofile.channel === undefined ? '' : userprofile.channel, desc: '', designation: designationValue, - email: userprofile.personalDetails.primaryEmail, - first_name: userprofile.personalDetails.firstname, - last_name: userprofile.personalDetails.middlename, - phone_No: userprofile.personalDetails.mobile, + email: primaryEmail, + first_name: userprofile.firstName, + last_name: userprofile.lastName, + phone_No: mobileNumber, userLocation: '', user_id: userprofile.id, } @@ -330,11 +338,19 @@ export interface ICohortsUser { } export interface IUserProfile { + channel: string + firstName: string + id: string + lastName: string + profileDetails: IUserProfileDetails +} + +export interface IUserProfileDetails { personalDetails: IPersonalDetails professionalDetails: IProfessionalDetailsEntity[] employmentDetails: IEmploymentDetails - id: string } + export interface IPersonalDetails { firstname: string middlename: string From a69a875a0901c724ff6b250ae3d474587a422834 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 25 Nov 2021 21:20:47 +0530 Subject: [PATCH 206/856] Passing proper object to get user information --- src/protectedApi_v8/cohorts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protectedApi_v8/cohorts.ts b/src/protectedApi_v8/cohorts.ts index d91f457c..9bff996e 100644 --- a/src/protectedApi_v8/cohorts.ts +++ b/src/protectedApi_v8/cohorts.ts @@ -274,7 +274,7 @@ cohortsApi.get('/course/getUsersForBatch/:batchId', async (req, res) => { }) if (searchresponse.data.result.response.count > 0) { for (const profileObj of searchresponse.data.result.response.content) { - const user: ICohortsUser = getUsers(profileObj.profileDetails) + const user: ICohortsUser = getUsers(profileObj) user.department = profileObj.channel userlist.push(user) } From 0b6d6b268e06defe1f31bced3bdbdd42ba2c9afd Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Tue, 30 Nov 2021 12:28:53 +0530 Subject: [PATCH 207/856] adding discussion api in whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index d87f2cc2..29b7d185 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1503,6 +1503,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/discussion/v2/categories':{ + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + } }, URL_PATTERN: [ @@ -1563,6 +1570,7 @@ export const API_LIST = { '/proxies/v8/learnervm/private/content/v3/review/:do_id', '/proxies/v8/discussion/v2/topics', '/proxies/v8/discussion/v2/topics/:id', + '/proxies/v8/discussion/v2/categories', '/proxies/v8/discussion/tags/:tag', '/proxies/v8/discussion/user/:userKey/bookmarks', '/proxies/v8/discussion/user/:userKey/bookmark', From 457f0dd6422f7f890b7c66f9b0271a991e68e30c Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Fri, 3 Dec 2021 17:18:35 +0530 Subject: [PATCH 208/856] Added Storage APIs in Proxy --- src/proxies_v8/proxies_v8.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index f2ffcd94..2a2da235 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -251,6 +251,10 @@ proxiesV8.use('/orghistory/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) +proxiesV8.use('/storage/*', + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + // proxiesV8.use('/api/framework/*', // // tslint:disable-next-line: max-line-length // proxyCreatorQML(express.Router(), `${CONSTANTS.KONG_API_BASE}`, '/api/') From 8904925b2d56f6dec3f7068c39beb7467304b66b Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Fri, 3 Dec 2021 17:21:20 +0530 Subject: [PATCH 209/856] Added Storage APIs in Whitelist --- src/utils/whitelistApis.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 29b7d185..9769f912 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1509,6 +1509,20 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.PUBLIC, ], + }, + '/proxies/v8/storage/upload':{ + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ], + }, + '/proxies/v8/storage/delete':{ + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ], } }, URL_PATTERN: @@ -1711,5 +1725,7 @@ export const API_LIST = { '/protected/v8/cohorts/course/batch/cert/download/:certId', '/protected/v8/cohorts/course/batch/cert/issue', '/protected/v8/cohorts/course/batch/cert/template/add', + '/proxies/v8/storage/upload', + '/proxies/v8/storage/delete' ], } From 15435d0e73390a31a22e8c5b4413222c7fe38d29 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Tue, 7 Dec 2021 13:39:58 +0530 Subject: [PATCH 210/856] Added API to read batch --- src/authoring/content/index.ts | 23 +++++++++++++++++++++++ src/utils/whitelistApis.ts | 19 ++++++++++++++----- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/authoring/content/index.ts b/src/authoring/content/index.ts index cb7e0f08..60b0ea17 100644 --- a/src/authoring/content/index.ts +++ b/src/authoring/content/index.ts @@ -31,6 +31,7 @@ const API_END_POINTS = { createBatch: `${CONSTANTS.KONG_API_BASE}/course/v1/batch/create`, downloadCert: `${CONSTANTS.KONG_API_BASE}/certreg/v1/certs/download`, issueCertToCourseBatch: `${CONSTANTS.KONG_API_BASE}/course/batch/cert/v1/issue?reIssue=true`, + readBatch: (batchId: string) => `${CONSTANTS.KONG_API_BASE}/course/v1/batch/read/${batchId}`, readCert: (certId: string) => `${CONSTANTS.KONG_API_BASE}/certreg/v2/certs/download/${certId}`, removeCertFromCourseBatch: `${CONSTANTS.KONG_API_BASE}/course/batch/cert/v1/template/remove`, updateBatch: `${CONSTANTS.KONG_API_BASE}/course/v1/batch/update`, @@ -254,6 +255,28 @@ authApi.patch('/content/v3/update/:id', async (req: Request, res: Response) => { }) }) +authApi.get('/readBatch/:batchId', async (req: Request, res: Response) => { + try { + const batchId = req.params.batchId + const response = await axios.get(API_END_POINTS.readBatch(batchId), { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), + }, + }) + res.status(response.status).send(response.data) + } catch (err) { + logError(failedToProcess + err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: ERROR.GENERAL_ERR_MSG, + } + ) + } +}) + authApi.post('/batch/:key', async (req: Request, res: Response) => { try { const key = req.params.key as string diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 9769f912..c484776c 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1347,6 +1347,14 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/authApi/readBatch/:batchId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.CONTENT_CREATOR, + ROLE.MDO_ADMIN, + ], + }, '/authApi/readCert/:certId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1503,33 +1511,34 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/proxies/v8/discussion/v2/categories':{ + '/proxies/v8/discussion/v2/categories': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/proxies/v8/storage/upload':{ + '/proxies/v8/storage/upload': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, ], }, - '/proxies/v8/storage/delete':{ + '/proxies/v8/storage/delete': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, ], - } + }, }, URL_PATTERN: [ // '/authApi/content/v3/create', // '/authApi/content/v3/read/:do_id', // '/authApi/content/v3/update/:do_id', + '/authApi/readBatch/:batchId', '/authApi/batch/:key', '/authApi/readCert/:certId', '/proxies/v8/api/user/v2/read', @@ -1726,6 +1735,6 @@ export const API_LIST = { '/protected/v8/cohorts/course/batch/cert/issue', '/protected/v8/cohorts/course/batch/cert/template/add', '/proxies/v8/storage/upload', - '/proxies/v8/storage/delete' + '/proxies/v8/storage/delete', ], } From 4067fabaac26d8816f92649bb20f23d24ce1fe7f Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 16 Dec 2021 10:26:06 +0530 Subject: [PATCH 211/856] Added Keycloak public key in configuration --- src/configs/keycloak.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/configs/keycloak.config.ts b/src/configs/keycloak.config.ts index 0956df05..efdd020e 100644 --- a/src/configs/keycloak.config.ts +++ b/src/configs/keycloak.config.ts @@ -8,5 +8,6 @@ export function getKeycloakConfig(url?: string, realm?: string) { realm: realm ? realm : CONSTANTS.KEYCLOAK_REALM, resource: 'portal', 'auth-server-url': url ? `${url}` : `${CONSTANTS.HTTPS_HOST}/auth`, + 'realm-public-key' : CONSTANTS.KEYCLOAK_PUBLIC_KEY, } } From 12436b9f4d0654fbe1b5eafa7b4342c8f327b6e8 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 16 Dec 2021 10:26:34 +0530 Subject: [PATCH 212/856] Added Keycloak public key in configuration --- src/utils/env.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/env.ts b/src/utils/env.ts index d3b5a3fb..c5555940 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -155,7 +155,8 @@ export const CONSTANTS = { VM_LEARNING_SERVICE_URL: env.VM_LEARNING_SERVICE_URL, // tslint:disable-next-line: max-line-length CERT_AUTH_TOKEN: 'bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJRekw4VVA1dUtqUFdaZVpMd1ZtTFJvNHdqWTg2a2FrcSJ9.TPjV0xLacSbp3FbJ7XeqHoKFN35Rl4YHx3DZNN9pm0o', - + // tslint:disable-next-line: max-line-length + KEYCLOAK_PUBLIC_KEY: 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoBa+pmsxpPhGED2/zexM/PL+rLv2YnLCbX2agY2fllucCvnPNmWRTyg8L+qgkJBpHuhcHqXEFTfBm2WCe4yke+0LnGi6SGsCJTrc2Sh+Lgo/L9r1lr7uOoD8BHE6ywnbHG2ok7qyomCTD9LpG1WXFk4T3WUH9+gN2vm77s3YJz2yfo2UlMd1Dp9nWZhfwB1vwJLuRHnqEKkSH8M2K8SFRdpgOyLdM9fprr7Ebgr7SWdoAy2q3mUONSmDsftKDeEWjr6MXGTATfbMfKE3GFqddMjFHkmvh42aM+RlU3N2xo2msF8M2sYF6YlIDkED+9h+y0QROJjQlBr774GAd06I0wIDAQAB', } export const RESTRICTED_PYTHON_STMT: string[] = process.env.RESTRICTED_CHARACTERS From 5627fdcce064934df69439c9fefcde621c5acb0c Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 16 Dec 2021 10:51:28 +0530 Subject: [PATCH 213/856] Updated keycloak connect version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f2e6b44b..2ce1d3dc 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "helmet": "^3.21.2", "http-proxy": "^1.17.0", "keycloak-admin": "^1.13.0", - "keycloak-connect": "^3.4.3", + "keycloak-connect": "^7.0.1", "lodash": "^4.17.21", "morgan": "^1.9.1", "node-xlsx": "^0.15.0", From 8f8c150622e2cbee53bb5aab0ff7636f22e7de48 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 16 Dec 2021 10:52:27 +0530 Subject: [PATCH 214/856] Removed realm public key --- src/configs/keycloak.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/configs/keycloak.config.ts b/src/configs/keycloak.config.ts index efdd020e..7bd50d64 100644 --- a/src/configs/keycloak.config.ts +++ b/src/configs/keycloak.config.ts @@ -8,6 +8,6 @@ export function getKeycloakConfig(url?: string, realm?: string) { realm: realm ? realm : CONSTANTS.KEYCLOAK_REALM, resource: 'portal', 'auth-server-url': url ? `${url}` : `${CONSTANTS.HTTPS_HOST}/auth`, - 'realm-public-key' : CONSTANTS.KEYCLOAK_PUBLIC_KEY, + 'confidential-port': 0, } } From 6637a9877b06f0c91536827e2157d4c81bb665fa Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Wed, 29 Dec 2021 01:54:12 +0530 Subject: [PATCH 215/856] Adding professionalDetails only when designation mentioned --- src/protectedApi_v8/user/profile-details.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/protectedApi_v8/user/profile-details.ts b/src/protectedApi_v8/user/profile-details.ts index 8294b942..f1d59d52 100644 --- a/src/protectedApi_v8/user/profile-details.ts +++ b/src/protectedApi_v8/user/profile-details.ts @@ -216,6 +216,7 @@ const createUserFailed = 'ERROR CREATING USER >' const failedToUpdateUser = 'Failed to update user profile data.' const unknownError = 'Failed due to unknown reason' +// tslint:disable-next-line: max-lines-per-function profileDeatailsApi.post('/createUser', async (req, res) => { try { const sbChannel = req.body.personalDetails.channel @@ -228,7 +229,6 @@ profileDeatailsApi.post('/createUser', async (req, res) => { const sbfirstName_ = req.body.personalDetails.firstName const sblastName_ = req.body.personalDetails.lastName const isEmailRequired = (req.body.personalDetails.isEmailRequired) ? req.body.personalDetails.isEmailRequired : true - const sbDesignation = (req.body.personalDetails.designation) ? req.body.personalDetails.designation : '' const searchresponse = await axios({ ...axiosRequestConfig, data: { request: { query: '', filters: { email: sbemail_.toLowerCase() } } }, @@ -313,12 +313,6 @@ profileDeatailsApi.post('/createUser', async (req, res) => { method: 'POST', url: API_END_POINTS.createNodeBBUser, }) - - const arrDesignation = [] - const objDesignation = { - designation: (sbDesignation) ? sbDesignation : '', - } - arrDesignation.push(objDesignation) const sbUserOrgId = sbUserReadResponse.data.result.response.rootOrgId const sbProfileUpdateReq = { profileDetails: { @@ -330,10 +324,18 @@ profileDeatailsApi.post('/createUser', async (req, res) => { primaryEmail: sbemail_, surname: sblastName_, }, - professionalDetails: arrDesignation, }, userId: sbUserId, } + if (req.body.personalDetails.designation) { + const arrDesignation = [] + const objDesignation = { + designation: (req.body.personalDetails.designation) ? req.body.personalDetails.designation : '', + } + arrDesignation.push(objDesignation) + const profDetailsPropertyName = 'professionalDetails' + sbProfileUpdateReq[profDetailsPropertyName] = arrDesignation + } const sbUserProfileUpdateResp = await axios({ ...axiosRequestConfig, From 9e0c768f9c7b0a290b11de8b24461b50d7783a70 Mon Sep 17 00:00:00 2001 From: nivetha Date: Thu, 30 Dec 2021 15:31:12 +0530 Subject: [PATCH 216/856] User batch progress API --- src/authoring/content/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/authoring/content/index.ts b/src/authoring/content/index.ts index 60b0ea17..2c7f185a 100644 --- a/src/authoring/content/index.ts +++ b/src/authoring/content/index.ts @@ -35,6 +35,7 @@ const API_END_POINTS = { readCert: (certId: string) => `${CONSTANTS.KONG_API_BASE}/certreg/v2/certs/download/${certId}`, removeCertFromCourseBatch: `${CONSTANTS.KONG_API_BASE}/course/batch/cert/v1/template/remove`, updateBatch: `${CONSTANTS.KONG_API_BASE}/course/v1/batch/update`, + getUserProgress: `${CONSTANTS.KONG_API_BASE}/v1/batch/getUserProgress`, } authApi.all('*', (req, _res, next) => { @@ -297,6 +298,9 @@ authApi.post('/batch/:key', async (req: Request, res: Response) => { case 'downloadCert': targetUrl = API_END_POINTS.downloadCert break + case 'getUserProgress': + targetUrl = API_END_POINTS.getUserProgress + break; default: logError('Unsupported operation received for batch API - ' + key) res.status(400).send('Unsupported operation name - ' + key) From 7430256940c82fb06e8d8b6f133f1de8704c405b Mon Sep 17 00:00:00 2001 From: nivetha Date: Thu, 30 Dec 2021 15:33:28 +0530 Subject: [PATCH 217/856] User batch progress API --- src/authoring/content/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/authoring/content/index.ts b/src/authoring/content/index.ts index 2c7f185a..bcecdced 100644 --- a/src/authoring/content/index.ts +++ b/src/authoring/content/index.ts @@ -30,12 +30,12 @@ const API_END_POINTS = { batchRemoveUser: `${CONSTANTS.KONG_API_BASE}/course/v1/unenrol`, createBatch: `${CONSTANTS.KONG_API_BASE}/course/v1/batch/create`, downloadCert: `${CONSTANTS.KONG_API_BASE}/certreg/v1/certs/download`, + getUserProgress: `${CONSTANTS.KONG_API_BASE}/v1/batch/getUserProgress`, issueCertToCourseBatch: `${CONSTANTS.KONG_API_BASE}/course/batch/cert/v1/issue?reIssue=true`, readBatch: (batchId: string) => `${CONSTANTS.KONG_API_BASE}/course/v1/batch/read/${batchId}`, readCert: (certId: string) => `${CONSTANTS.KONG_API_BASE}/certreg/v2/certs/download/${certId}`, removeCertFromCourseBatch: `${CONSTANTS.KONG_API_BASE}/course/batch/cert/v1/template/remove`, updateBatch: `${CONSTANTS.KONG_API_BASE}/course/v1/batch/update`, - getUserProgress: `${CONSTANTS.KONG_API_BASE}/v1/batch/getUserProgress`, } authApi.all('*', (req, _res, next) => { @@ -300,7 +300,7 @@ authApi.post('/batch/:key', async (req: Request, res: Response) => { break case 'getUserProgress': targetUrl = API_END_POINTS.getUserProgress - break; + break default: logError('Unsupported operation received for batch API - ' + key) res.status(400).send('Unsupported operation name - ' + key) From a661f0ea534d4d4ed037ba47a8a1be5238515582 Mon Sep 17 00:00:00 2001 From: "christopher.fernandes" Date: Wed, 5 Jan 2022 12:13:24 +0530 Subject: [PATCH 218/856] Task #0000: Fixed the session ttl in env --- src/utils/env.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/env.ts b/src/utils/env.ts index c5555940..d04ecba8 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -49,7 +49,8 @@ export const CONSTANTS = { // tslint:disable-next-line: object-literal-sort-keys KC_NEW_USER_DEFAULT_PWD: env.KC_NEW_USER_DEFAULT_PWD || 'User@123', KEYCLOAK_REALM: env.KEYCLOAK_REALM || 'sunbird', - KEYCLOAK_SESSION_TTL: 24 * 60 * 60 * 1000, + // tslint:disable-next-line: ban + KEYCLOAK_SESSION_TTL: parseInt(env.KEYCLOAK_SESSION_TTL || '0', 10) || 24 * 60 * 60 * 1000, KHUB_CLIENT_SECRET: env.KHUB_CLIENT_SECRET || 'axc123', KHUB_GRAPH_DATA: env.KHUB_GRAPH_DATA || 'http://localhost:3016', KHUB_SEARCH_BASE: env.KHUB_SEARCH_BASE || 'http://localhost:3014', From d1ecf0e7bc5d8a9b45e83022dbdae5b4520f4e77 Mon Sep 17 00:00:00 2001 From: nivetha Date: Wed, 5 Jan 2022 18:37:08 +0530 Subject: [PATCH 219/856] User batch enrolment API --- src/authoring/content/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/authoring/content/index.ts b/src/authoring/content/index.ts index bcecdced..e6390f5b 100644 --- a/src/authoring/content/index.ts +++ b/src/authoring/content/index.ts @@ -26,8 +26,8 @@ const actionConst = '/action' const API_END_POINTS = { addCertToCourseBatch: `${CONSTANTS.KONG_API_BASE}/course/batch/cert/v1/template/add`, - batchAddUser: `${CONSTANTS.KONG_API_BASE}/course/v1/enrol`, - batchRemoveUser: `${CONSTANTS.KONG_API_BASE}/course/v1/unenrol`, + batchAddUser: `${CONSTANTS.KONG_API_BASE}/course/v1/admin/enrol`, + batchRemoveUser: `${CONSTANTS.KONG_API_BASE}/course/v1/admin/unenrol`, createBatch: `${CONSTANTS.KONG_API_BASE}/course/v1/batch/create`, downloadCert: `${CONSTANTS.KONG_API_BASE}/certreg/v1/certs/download`, getUserProgress: `${CONSTANTS.KONG_API_BASE}/v1/batch/getUserProgress`, From efd1221f45c25210a3a7c0192a4c03ecfab0ebe2 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Thu, 6 Jan 2022 13:11:17 +0530 Subject: [PATCH 220/856] Added permission to SPV_ADMIN for user block / unblock --- src/utils/whitelistApis.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index c484776c..1955497b 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -380,6 +380,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/user/v1/unblock': { @@ -387,6 +388,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/data/v1/system/settings/get/orgTypeConfig': { From d8da818a90cc814dc0561fde797b418758feb21c Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Mon, 10 Jan 2022 16:38:22 +0530 Subject: [PATCH 221/856] Excluding Storage Upload API for Req body modification --- src/utils/proxyCreator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index a46c0544..6834721e 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -32,7 +32,7 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { console.log('REQ_URL_ORIGINAL discussion', proxyReq.path) } - if (req.body) { + if (!req.originalUrl.includes('/storage/upload') && req.body) { const bodyData = JSON.stringify(req.body) proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData)) proxyReq.write(bodyData) From 8b409c7b37ef7f6191268fcdaf33559754ddb394 Mon Sep 17 00:00:00 2001 From: Mathew Jose Pallan Date: Wed, 12 Jan 2022 16:57:57 +0530 Subject: [PATCH 222/856] Removing async behavior from the authenticated method Experimental change that may need to be reverted --- src/utils/custom-keycloak.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/custom-keycloak.ts b/src/utils/custom-keycloak.ts index cfa689fb..9c8e0091 100644 --- a/src/utils/custom-keycloak.ts +++ b/src/utils/custom-keycloak.ts @@ -53,7 +53,7 @@ export class CustomKeycloak { } // tslint:disable-next-line: no-any - authenticated = async (request: any) => { + authenticated = (request: any) => { // console.log('Step 3: authenticated function') try { const userId = request.kauth.grant.access_token.content.sub.split(':') From a474df637b74a1c7f4e5ac3584a2e6447899bd92 Mon Sep 17 00:00:00 2001 From: "christopher.fernandes" Date: Thu, 13 Jan 2022 12:54:25 +0530 Subject: [PATCH 223/856] Task #0000: Added logs for debugging login issue for some users --- src/protectedApi_v8/resource.ts | 4 ++++ src/server.ts | 2 ++ src/utils/apiWhiteList.ts | 7 ++++++- src/utils/custom-keycloak.ts | 30 ++++++++++++++++++++++++++++-- src/utils/permissionHelper.ts | 26 ++++++++++++++++++++------ 5 files changed, 60 insertions(+), 9 deletions(-) diff --git a/src/protectedApi_v8/resource.ts b/src/protectedApi_v8/resource.ts index 10fbfaf0..48d6d8d5 100644 --- a/src/protectedApi_v8/resource.ts +++ b/src/protectedApi_v8/resource.ts @@ -2,6 +2,8 @@ import { Router } from 'express' const _ = require('lodash') export const userAuthKeyCloakApi = Router() userAuthKeyCloakApi.get('/', async (req, res) => { + // tslint:disable-next-line: no-console + console.log('userAuthKeyCloakApi.get --- ') const host = req.get('host') let queryParam = '' let isLocal = 0 @@ -17,5 +19,7 @@ userAuthKeyCloakApi.get('/', async (req, res) => { } else { redirectUrl = `https://${host}${queryParam}` // 'https://' + host + '/page/home' } + // tslint:disable-next-line: no-console + console.log('userAuthKeyCloakApi.get:: redirect url --- ', `https://${host}${queryParam}`) res.redirect(redirectUrl) }) diff --git a/src/server.ts b/src/server.ts index 380a7e7b..c3c018af 100644 --- a/src/server.ts +++ b/src/server.ts @@ -131,6 +131,8 @@ export class Server { } // tslint:disable-next-line: no-any private setKeyCloak(sessionConfig: any) { + // tslint:disable-next-line: no-console + console.log('setKeyCloak') this.keycloak = new CustomKeycloak(sessionConfig) this.app.use(this.keycloak.middleware) } diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index 3e02eeab..27087c6d 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -202,7 +202,8 @@ const executeChecks = async (req: Request, res: Response , next: NextFunction, c respond403(req, res) }) } catch (error) { - logError(error) + // tslint:disable-next-line: no-console + console.log('ERROR --', error) respond403(req, res) } } @@ -335,6 +336,8 @@ export const isAllowed = () => { const redirectToLogin = (req: Request) => { const redirectUrl = 'protected/v8/resource/' return `https://${req.get('host')}/${redirectUrl}` // 'http://localhost:3003/protected/v8/user/resource/' + // tslint:disable-next-line: no-commented-code + // return 'http://localhost:3003/protected/v8/user/resource/' } const validateAPI = (req: Request, res: Response, next: NextFunction) => { @@ -361,6 +364,8 @@ const validateAPI = (req: Request, res: Response, next: NextFunction) => { */ export function apiWhiteListLogger() { return (req: Request, res: Response, next: NextFunction) => { + // tslint:disable-next-line: no-console + console.log('req.query:: ', req.query) if (req.path === '/' || checkIsStaticRoute(req.path)) { next() return diff --git a/src/utils/custom-keycloak.ts b/src/utils/custom-keycloak.ts index cfa689fb..225c9d2a 100644 --- a/src/utils/custom-keycloak.ts +++ b/src/utils/custom-keycloak.ts @@ -13,6 +13,10 @@ export class CustomKeycloak { private multiTenantKeycloak = new Map() constructor(sessionConfig: expressSession.SessionOptions) { + // tslint:disable-next-line: no-console + console.log('custom-keycloak::constructor start ') + // tslint:disable-next-line: no-console + console.log('CONSTANTS.MULTI_TENANT_KEYCLOAK -- ', CONSTANTS.MULTI_TENANT_KEYCLOAK) if (CONSTANTS.MULTI_TENANT_KEYCLOAK) { CONSTANTS.MULTI_TENANT_KEYCLOAK.split(';').forEach((v: string) => { const domainUrlMap = v.split(',') @@ -23,6 +27,8 @@ export class CustomKeycloak { }) } this.multiTenantKeycloak.set('common', this.generateKeyCloak(sessionConfig)) + // tslint:disable-next-line: no-console + console.log('custom-keycloak::constructor end ') } middleware = (req: express.Request, res: express.Response, next: express.NextFunction) => { @@ -37,6 +43,12 @@ export class CustomKeycloak { } getKeyCloakObject(req: express.Request): keycloakConnect { + // tslint:disable-next-line: no-console + console.log('custom-keycloak::getKeyCloakObject') + // tslint:disable-next-line: no-console + console.log(`req.header('rootOrg') -- `, req.headers && req.header('rootOrg')) + // tslint:disable-next-line: no-console + console.log(`req.cookies.rootorg -- `, req.cookies && req.cookies.rootorg) const rootOrg = (req.headers ? req.header('rootOrg') : '') || (req.cookies ? req.cookies.rootorg : '') let domain = '' @@ -47,17 +59,27 @@ export class CustomKeycloak { } }) } + // tslint:disable-next-line: no-console + console.log(`custom-keycloak::getKeyCloakObject before retun value -- `, this.multiTenantKeycloak.get(req.hostname) || + this.multiTenantKeycloak.get(domain) || + this.multiTenantKeycloak.get('common')) + return (this.multiTenantKeycloak.get(req.hostname) || this.multiTenantKeycloak.get(domain) || this.multiTenantKeycloak.get('common')) as keycloakConnect } // tslint:disable-next-line: no-any - authenticated = async (request: any) => { - // console.log('Step 3: authenticated function') + authenticated = (request: any) => { + // tslint:disable-next-line: no-console + console.log('Step 3: authenticated function') try { const userId = request.kauth.grant.access_token.content.sub.split(':') request.session.userId = userId[userId.length - 1] + // tslint:disable-next-line: no-console + console.log('userId ::', userId) + // tslint:disable-next-line: no-console + console.log('request.session after adding userId ::', request.session) } catch (err) { logError('userId conversation error' + request.kauth.grant.access_token.content.sub) } @@ -87,8 +109,12 @@ export class CustomKeycloak { } protect = (req: express.Request, res: express.Response, next: express.NextFunction) => { + // tslint:disable-next-line: no-console + console.log('custom-keycloak::protect start') const keycloak = this.getKeyCloakObject(req) keycloak.protect()(req, res, next) + // tslint:disable-next-line: no-console + console.log('custom-keycloak::protect end') } private generateKeyCloak( diff --git a/src/utils/permissionHelper.ts b/src/utils/permissionHelper.ts index cdc7453b..aaaad117 100644 --- a/src/utils/permissionHelper.ts +++ b/src/utils/permissionHelper.ts @@ -3,12 +3,13 @@ import axios from 'axios' import request from 'request' import { axiosRequestConfig } from '../configs/request.config' import { CONSTANTS } from './env' -import { logInfo } from './logger' +import { logError, logInfo } from './logger' import { extractUserToken } from './requestExtract' export const PERMISSION_HELPER = { // tslint:disable-next-line: no-any setRolesData(reqObj: any, callback: any, body: any) { + logInfo('permission helper:: setRolesData function ') // tslint:disable-next-line: no-any const userData: any = JSON.parse(body) logInfo(JSON.stringify(userData)) @@ -26,33 +27,42 @@ export const PERMISSION_HELPER = { // tslint:disable-next-line: no-any reqObj.session.save((error: any) => { if (error) { - callback(error, null) + logError('reqObj.session.save error -- ', error) + callback(error, null) } else { + logInfo('Before calling createNodeBBUser') this.createNodeBBUser(reqObj, callback) callback(null, userData) } }) } + logInfo('permission helper:: setRolesData function end') }, // tslint:disable-next-line: no-any setNodeBBUID(reqObj: any, callback: any, body: any) { + logInfo('permissionHelper:: setNodeBBUID function start') // tslint:disable-next-line: no-any const nodeBBData: any = body if (reqObj.session) { reqObj.session.uid = nodeBBData.data.result.userId.uid + logInfo('After appending uid to session', reqObj.session.uid) } // tslint:disable-next-line: no-any reqObj.session.save((error: any) => { if (error) { + logError('reqObj.session.save error -- ', error) callback(error, null) } }) + logInfo('permissionHelper:: setNodeBBUID function end') }, // tslint:disable-next-line: no-any getCurrentUserRoles(reqObj: any, callback: any) { - // console.log('Step 3: Get user roles function') + logInfo('Step 3: Get user roles function') const userId = reqObj.session.userId - // console.log(userId) + // tslint:disable-next-line: no-console + console.log(userId) + logInfo('Calling user/v2/read') const readUrl = `${CONSTANTS.KONG_API_BASE}/user/v2/read/` + userId const options = { headers: { @@ -65,13 +75,17 @@ export const PERMISSION_HELPER = { } // tslint:disable-next-line: no-any request.get(options, (_err: any, _httpResponse: any, body: any) => { + logInfo('Success user/v2/read') if (body) { + // tslint:disable-next-line: no-console + console.log('Success user/v2/read: body', body) this.setRolesData(reqObj, callback, body) } }) }, // tslint:disable-next-line: no-any async createNodeBBUser(reqObj: any, callback: any) { + logInfo('permissionHelper::createNodeBBUser function start') const readUrl = `${CONSTANTS.KONG_API_BASE}/discussion/user/v1/create` // tslint:disable-next-line: no-commented-code @@ -81,7 +95,7 @@ export const PERMISSION_HELPER = { identifier: reqObj.session.userId, fullname: reqObj.session.firstName + ' ' + reqObj.session.lastName, } - + logInfo('Making axios call to nodeBB') const nodeBBResp = await axios({ ...axiosRequestConfig, data: { request: nodebbPayload }, @@ -93,9 +107,9 @@ export const PERMISSION_HELPER = { method: 'POST', url: readUrl, }) - if (nodeBBResp) { this.setNodeBBUID(reqObj, callback, nodeBBResp) } + logInfo('permissionHelper::createNodeBBUser function end') }, } From 1be637f7a86f1edc936b4cf3b04b4fcf06525085 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Thu, 13 Jan 2022 13:59:51 +0530 Subject: [PATCH 224/856] Added API for user extended patch --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 1955497b..2250ecde 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1534,6 +1534,13 @@ export const API_LIST = { ROLE.MDO_ADMIN, ], }, + '/proxies/v8/user/v1/extPatch': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ], + }, }, URL_PATTERN: [ @@ -1738,5 +1745,6 @@ export const API_LIST = { '/protected/v8/cohorts/course/batch/cert/template/add', '/proxies/v8/storage/upload', '/proxies/v8/storage/delete', + '/proxies/v8/user/v1/extPatch', ], } From 69870373cfc11c95902530dbeb064fe6b8cbeb7f Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Thu, 13 Jan 2022 14:10:18 +0530 Subject: [PATCH 225/856] Updated extPatch API Role access --- src/utils/whitelistApis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 2250ecde..6efd78c5 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1538,7 +1538,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, + ROLE.PUBLIC, ], }, }, From f9d51cb9d49909ce1c1ed35c9af1c7333de1546c Mon Sep 17 00:00:00 2001 From: "christopher.fernandes" Date: Thu, 13 Jan 2022 20:38:01 +0530 Subject: [PATCH 226/856] Task #0000: more logs for debugging login issue for some users --- src/protectedApi_v8/resource.ts | 2 +- src/utils/custom-keycloak.ts | 26 ++++++++++++------ src/utils/permissionHelper.ts | 47 ++++++++++++++++++++------------- src/utils/proxyCreator.ts | 5 ++++ 4 files changed, 53 insertions(+), 27 deletions(-) diff --git a/src/protectedApi_v8/resource.ts b/src/protectedApi_v8/resource.ts index 48d6d8d5..60b89e88 100644 --- a/src/protectedApi_v8/resource.ts +++ b/src/protectedApi_v8/resource.ts @@ -1,7 +1,7 @@ import { Router } from 'express' const _ = require('lodash') export const userAuthKeyCloakApi = Router() -userAuthKeyCloakApi.get('/', async (req, res) => { +userAuthKeyCloakApi.get('/', (req, res) => { // tslint:disable-next-line: no-console console.log('userAuthKeyCloakApi.get --- ') const host = req.get('host') diff --git a/src/utils/custom-keycloak.ts b/src/utils/custom-keycloak.ts index 225c9d2a..b97cfe77 100644 --- a/src/utils/custom-keycloak.ts +++ b/src/utils/custom-keycloak.ts @@ -70,7 +70,7 @@ export class CustomKeycloak { } // tslint:disable-next-line: no-any - authenticated = (request: any) => { + authenticated = async (request: any) => { // tslint:disable-next-line: no-console console.log('Step 3: authenticated function') try { @@ -91,13 +91,23 @@ export class CustomKeycloak { }) // tslint:disable-next-line: no-any - async.series(postLoginRequest, (err: any) => { - if (err) { - logError('error loggin in user') - } else { - logInfo(`${process.pid}: User authenticated`) - } - }) + try { + // tslint:disable-next-line: no-any + const results = await async.series(postLoginRequest, (err: any) => { + if (err) { + logError('error loggin in user') + // tslint:disable-next-line: no-console + console.log('ERROR loggin in user ---- ', err) + } else { + logInfo(`${process.pid}: User authenticated`) + } + }) + // tslint:disable-next-line: no-console + console.log('async.series::results ---- ', results) + } catch (err) { + // tslint:disable-next-line: no-console + console.log('ERROR in aync series ---- ', err) + } } // tslint:disable-next-line: no-any diff --git a/src/utils/permissionHelper.ts b/src/utils/permissionHelper.ts index aaaad117..05753995 100644 --- a/src/utils/permissionHelper.ts +++ b/src/utils/permissionHelper.ts @@ -62,7 +62,7 @@ export const PERMISSION_HELPER = { const userId = reqObj.session.userId // tslint:disable-next-line: no-console console.log(userId) - logInfo('Calling user/v2/read') + logInfo('Calling user/v2/read ') const readUrl = `${CONSTANTS.KONG_API_BASE}/user/v2/read/` + userId const options = { headers: { @@ -74,13 +74,18 @@ export const PERMISSION_HELPER = { url: readUrl, } // tslint:disable-next-line: no-any - request.get(options, (_err: any, _httpResponse: any, body: any) => { - logInfo('Success user/v2/read') + request.get(options, (err: any, _httpResponse: any, body: any) => { + logInfo('Inside user/v2/read') if (body) { // tslint:disable-next-line: no-console console.log('Success user/v2/read: body', body) this.setRolesData(reqObj, callback, body) } + if (err) { + // tslint:disable-next-line: no-console + console.log('Making axios call to nodeBB ERROR -- ', err) + callback(err, null) + } }) }, // tslint:disable-next-line: no-any @@ -96,20 +101,26 @@ export const PERMISSION_HELPER = { fullname: reqObj.session.firstName + ' ' + reqObj.session.lastName, } logInfo('Making axios call to nodeBB') - const nodeBBResp = await axios({ - ...axiosRequestConfig, - data: { request: nodebbPayload }, - headers: { - Authorization: CONSTANTS.SB_API_KEY, - // tslint:disable-next-line: all - 'x-authenticated-user-token': extractUserToken(reqObj), - }, - method: 'POST', - url: readUrl, - }) - if (nodeBBResp) { - this.setNodeBBUID(reqObj, callback, nodeBBResp) - } - logInfo('permissionHelper::createNodeBBUser function end') + try { + const nodeBBResp = await axios({ + ...axiosRequestConfig, + data: { request: nodebbPayload }, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(reqObj), + }, + method: 'POST', + url: readUrl, + }) + if (nodeBBResp) { + this.setNodeBBUID(reqObj, callback, nodeBBResp) + } + logInfo('permissionHelper::createNodeBBUser function end') + } catch (err) { + // tslint:disable-next-line: no-console + console.log('Making axios call to nodeBB ERROR -- ', err) + callback(err, null) + } }, } diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 6834721e..41b1b170 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -216,6 +216,11 @@ export function proxyCreatorSunbirdSearch(route: Router, targetUrl: string, _tim } export function proxyCreatorToAppentUserId(route: Router, targetUrl: string, _timeout = 10000): Router { + logInfo('proxyCreatorToAppentUserId Function ----') + // tslint:disable-next-line: no-console + console.log('proxyCreatorToAppentUserId Function:: route ----', route) + // tslint:disable-next-line: no-console + console.log('proxyCreatorToAppentUserId Function:: targetUrl ----', targetUrl) route.all('/*', (req, res) => { const originalUrl = req.originalUrl const lastIndex = originalUrl.lastIndexOf('/') From 649d7d4a72e8016eb649492dead9f64b9d49edf5 Mon Sep 17 00:00:00 2001 From: "christopher.fernandes" Date: Mon, 17 Jan 2022 17:02:53 +0530 Subject: [PATCH 227/856] Task #0000: more logs for debugging login issue for some users --- src/utils/custom-keycloak.ts | 17 +++++++++-------- src/utils/permissionHelper.ts | 22 +++++++++++++--------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/utils/custom-keycloak.ts b/src/utils/custom-keycloak.ts index b97cfe77..ba25ccc0 100644 --- a/src/utils/custom-keycloak.ts +++ b/src/utils/custom-keycloak.ts @@ -72,7 +72,7 @@ export class CustomKeycloak { // tslint:disable-next-line: no-any authenticated = async (request: any) => { // tslint:disable-next-line: no-console - console.log('Step 3: authenticated function') + console.log('Step 3: authenticated function', '------', new Date()) try { const userId = request.kauth.grant.access_token.content.sub.split(':') request.session.userId = userId[userId.length - 1] @@ -86,24 +86,27 @@ export class CustomKeycloak { const postLoginRequest = [] // tslint:disable-next-line: no-any postLoginRequest.push((callback: any) => { - // console.log('in pus') + // tslint:disable-next-line: no-console + console.log('pushing task to postLoginRequest', '------', new Date()) PERMISSION_HELPER.getCurrentUserRoles(request, callback) }) // tslint:disable-next-line: no-any try { // tslint:disable-next-line: no-any - const results = await async.series(postLoginRequest, (err: any) => { + const results = await async.series(postLoginRequest, (err: any, result: any) => { if (err) { logError('error loggin in user') // tslint:disable-next-line: no-console console.log('ERROR loggin in user ---- ', err) } else { + // tslint:disable-next-line: no-console + console.log('async.series result --- ', result, '------', new Date()) logInfo(`${process.pid}: User authenticated`) } }) // tslint:disable-next-line: no-console - console.log('async.series::results ---- ', results) + console.log('async.series::results ---- ', results, '------', new Date().toString()) } catch (err) { // tslint:disable-next-line: no-console console.log('ERROR in aync series ---- ', err) @@ -120,11 +123,9 @@ export class CustomKeycloak { protect = (req: express.Request, res: express.Response, next: express.NextFunction) => { // tslint:disable-next-line: no-console - console.log('custom-keycloak::protect start') + console.log('custom-keycloak::protect start', '------', new Date()) const keycloak = this.getKeyCloakObject(req) - keycloak.protect()(req, res, next) - // tslint:disable-next-line: no-console - console.log('custom-keycloak::protect end') + return keycloak.protect()(req, res, next) } private generateKeyCloak( diff --git a/src/utils/permissionHelper.ts b/src/utils/permissionHelper.ts index 05753995..62892850 100644 --- a/src/utils/permissionHelper.ts +++ b/src/utils/permissionHelper.ts @@ -9,7 +9,7 @@ import { extractUserToken } from './requestExtract' export const PERMISSION_HELPER = { // tslint:disable-next-line: no-any setRolesData(reqObj: any, callback: any, body: any) { - logInfo('permission helper:: setRolesData function ') + logInfo('permission helper:: setRolesData function ', '------', new Date().toString()) // tslint:disable-next-line: no-any const userData: any = JSON.parse(body) logInfo(JSON.stringify(userData)) @@ -30,7 +30,7 @@ export const PERMISSION_HELPER = { logError('reqObj.session.save error -- ', error) callback(error, null) } else { - logInfo('Before calling createNodeBBUser') + logInfo('Before calling createNodeBBUser', '------', new Date().toString()) this.createNodeBBUser(reqObj, callback) callback(null, userData) } @@ -40,7 +40,7 @@ export const PERMISSION_HELPER = { }, // tslint:disable-next-line: no-any setNodeBBUID(reqObj: any, callback: any, body: any) { - logInfo('permissionHelper:: setNodeBBUID function start') + logInfo('permissionHelper:: setNodeBBUID function start', '------', new Date().toString()) // tslint:disable-next-line: no-any const nodeBBData: any = body if (reqObj.session) { @@ -54,15 +54,15 @@ export const PERMISSION_HELPER = { callback(error, null) } }) - logInfo('permissionHelper:: setNodeBBUID function end') + logInfo('permissionHelper:: setNodeBBUID function end', '------', new Date().toString()) }, // tslint:disable-next-line: no-any getCurrentUserRoles(reqObj: any, callback: any) { - logInfo('Step 3: Get user roles function') + logInfo('Step 3: Get user roles function', '------', new Date().toString()) const userId = reqObj.session.userId // tslint:disable-next-line: no-console console.log(userId) - logInfo('Calling user/v2/read ') + logInfo('Calling user/v2/read ', '------', new Date().toString()) const readUrl = `${CONSTANTS.KONG_API_BASE}/user/v2/read/` + userId const options = { headers: { @@ -75,8 +75,10 @@ export const PERMISSION_HELPER = { } // tslint:disable-next-line: no-any request.get(options, (err: any, _httpResponse: any, body: any) => { - logInfo('Inside user/v2/read') + logInfo('Inside user/v2/read', '------', new Date().toString()) if (body) { + // tslint:disable-next-line: no-console + console.log('------', new Date().toString()) // tslint:disable-next-line: no-console console.log('Success user/v2/read: body', body) this.setRolesData(reqObj, callback, body) @@ -90,7 +92,7 @@ export const PERMISSION_HELPER = { }, // tslint:disable-next-line: no-any async createNodeBBUser(reqObj: any, callback: any) { - logInfo('permissionHelper::createNodeBBUser function start') + logInfo('permissionHelper::createNodeBBUser function start', '------', new Date().toString()) const readUrl = `${CONSTANTS.KONG_API_BASE}/discussion/user/v1/create` // tslint:disable-next-line: no-commented-code @@ -100,7 +102,7 @@ export const PERMISSION_HELPER = { identifier: reqObj.session.userId, fullname: reqObj.session.firstName + ' ' + reqObj.session.lastName, } - logInfo('Making axios call to nodeBB') + logInfo('Making axios call to nodeBB', '------', new Date().toString()) try { const nodeBBResp = await axios({ ...axiosRequestConfig, @@ -114,6 +116,8 @@ export const PERMISSION_HELPER = { url: readUrl, }) if (nodeBBResp) { + // tslint:disable-next-line: no-console + console.log('NodeBB axios call response', '------', new Date().toString()) this.setNodeBBUID(reqObj, callback, nodeBBResp) } logInfo('permissionHelper::createNodeBBUser function end') From 2e9a560e5cc76fcb8f5e84b3f06466df42472335 Mon Sep 17 00:00:00 2001 From: "christopher.fernandes" Date: Mon, 17 Jan 2022 17:14:45 +0530 Subject: [PATCH 228/856] Task #0000: more logs for debugging login issue for some users --- src/utils/permissionHelper.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/permissionHelper.ts b/src/utils/permissionHelper.ts index 62892850..6b08e13d 100644 --- a/src/utils/permissionHelper.ts +++ b/src/utils/permissionHelper.ts @@ -35,6 +35,8 @@ export const PERMISSION_HELPER = { callback(null, userData) } }) + } else { + callback('reqObj.session no session', null) } logInfo('permission helper:: setRolesData function end') }, From 1d8767afedf54e19692dfc53ff688985400973d5 Mon Sep 17 00:00:00 2001 From: "christopher.fernandes" Date: Tue, 18 Jan 2022 11:44:23 +0530 Subject: [PATCH 229/856] Task #0000: Updated the keycloak changes --- package-lock.json | 66 +++++++++++++++++------------------- package.json | 3 +- src/utils/custom-keycloak.ts | 30 ++++++---------- 3 files changed, 43 insertions(+), 56 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8e5506df..3cdc6400 100644 --- a/package-lock.json +++ b/package-lock.json @@ -237,15 +237,6 @@ "@types/node": "*" } }, - "@types/keycloak-connect": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@types/keycloak-connect/-/keycloak-connect-4.5.1.tgz", - "integrity": "sha512-ioHJ+cZ3r8tfiaP/7aZv1ZJjx3WIA6bwexap7aTa5ioQDiLLHnJzwSeUZNfFm1Cj4J4KVsDsDBy33cY3iaelmg==", - "dev": true, - "requires": { - "@types/express": "*" - } - }, "@types/lodash": { "version": "4.14.170", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.170.tgz", @@ -662,13 +653,14 @@ } }, "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", "requires": { "bn.js": "^4.0.0", "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" } }, "assert-plus": { @@ -878,9 +870,9 @@ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" }, "body-parser": { "version": "1.19.0", @@ -2197,17 +2189,24 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "elliptic": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.0.tgz", - "integrity": "sha512-eFOJTMyCYb7xtE/caJ6JJu+bhi67WCYNbkGSknu20pmM8Ke/bqOfdnZWxyoGN26JgfxTbXrsCkEw4KheCT/KGg==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", + "bn.js": "^4.11.9", + "brorand": "^1.1.0", "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + } } }, "encodeurl": { @@ -4560,12 +4559,12 @@ "dev": true }, "jwk-to-pem": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/jwk-to-pem/-/jwk-to-pem-1.2.6.tgz", - "integrity": "sha1-1QfOzkAInFJI4J7GgmaiAwqcYyU=", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/jwk-to-pem/-/jwk-to-pem-2.0.5.tgz", + "integrity": "sha512-L90jwellhO8jRKYwbssU9ifaMVqajzj3fpRjDKcsDzrslU9syRbFqfkXtT4B89HYAap+xsxNcxgBSB09ig+a7A==", "requires": { - "asn1.js": "^4.5.2", - "elliptic": "^6.2.3", + "asn1.js": "^5.3.0", + "elliptic": "^6.5.4", "safe-buffer": "^5.0.1" } }, @@ -4596,11 +4595,10 @@ } }, "keycloak-connect": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/keycloak-connect/-/keycloak-connect-3.4.3.tgz", - "integrity": "sha1-Z3W1mRJdJNUk9uZKXlEv2gBpLE4=", + "version": "git+https://github.com/sunbird-cb/keycloak-nodejs-connect.git#536a6816b990257ec77bd8f576538aff8ba10283", + "from": "git+https://github.com/sunbird-cb/keycloak-nodejs-connect.git#release-2.5.0", "requires": { - "jwk-to-pem": "^1.2.6" + "jwk-to-pem": "^2.0.0" } }, "keyv": { diff --git a/package.json b/package.json index 2ce1d3dc..927bbda5 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "helmet": "^3.21.2", "http-proxy": "^1.17.0", "keycloak-admin": "^1.13.0", - "keycloak-connect": "^7.0.1", + "keycloak-connect": "git+https://github.com/sunbird-cb/keycloak-nodejs-connect.git#release-2.5.0", "lodash": "^4.17.21", "morgan": "^1.9.1", "node-xlsx": "^0.15.0", @@ -59,7 +59,6 @@ "@types/express-session": "^1.15.12", "@types/helmet": "0.0.43", "@types/http-proxy": "^1.17.0", - "@types/keycloak-connect": "^4.5.1", "@types/lodash": "^4.14.170", "@types/morgan": "^1.7.35", "@types/node": "^12.0.4", diff --git a/src/utils/custom-keycloak.ts b/src/utils/custom-keycloak.ts index ba25ccc0..ece2df68 100644 --- a/src/utils/custom-keycloak.ts +++ b/src/utils/custom-keycloak.ts @@ -70,7 +70,7 @@ export class CustomKeycloak { } // tslint:disable-next-line: no-any - authenticated = async (request: any) => { + authenticated = (request: any, next: any) => { // tslint:disable-next-line: no-console console.log('Step 3: authenticated function', '------', new Date()) try { @@ -92,25 +92,15 @@ export class CustomKeycloak { }) // tslint:disable-next-line: no-any - try { - // tslint:disable-next-line: no-any - const results = await async.series(postLoginRequest, (err: any, result: any) => { - if (err) { - logError('error loggin in user') - // tslint:disable-next-line: no-console - console.log('ERROR loggin in user ---- ', err) - } else { - // tslint:disable-next-line: no-console - console.log('async.series result --- ', result, '------', new Date()) - logInfo(`${process.pid}: User authenticated`) - } - }) - // tslint:disable-next-line: no-console - console.log('async.series::results ---- ', results, '------', new Date().toString()) - } catch (err) { - // tslint:disable-next-line: no-console - console.log('ERROR in aync series ---- ', err) - } + return async.series(postLoginRequest, (err: any) => { + if (err) { + logError('error loggin in user') + next(err, null) + } else { + logInfo(`${process.pid}: User authenticated`) + next(null, 'loggedin') + } + }) } // tslint:disable-next-line: no-any From 74697ad276bafd81c2fad8c45ee9a351735b6afa Mon Sep 17 00:00:00 2001 From: "christopher.fernandes" Date: Tue, 18 Jan 2022 13:33:24 +0530 Subject: [PATCH 230/856] Task #0000: cleaned up all the logs --- src/protectedApi_v8/resource.ts | 4 ---- src/server.ts | 2 -- src/utils/custom-keycloak.ts | 27 +-------------------------- src/utils/permissionHelper.ts | 19 ------------------- 4 files changed, 1 insertion(+), 51 deletions(-) diff --git a/src/protectedApi_v8/resource.ts b/src/protectedApi_v8/resource.ts index 60b89e88..36d283e0 100644 --- a/src/protectedApi_v8/resource.ts +++ b/src/protectedApi_v8/resource.ts @@ -2,8 +2,6 @@ import { Router } from 'express' const _ = require('lodash') export const userAuthKeyCloakApi = Router() userAuthKeyCloakApi.get('/', (req, res) => { - // tslint:disable-next-line: no-console - console.log('userAuthKeyCloakApi.get --- ') const host = req.get('host') let queryParam = '' let isLocal = 0 @@ -19,7 +17,5 @@ userAuthKeyCloakApi.get('/', (req, res) => { } else { redirectUrl = `https://${host}${queryParam}` // 'https://' + host + '/page/home' } - // tslint:disable-next-line: no-console - console.log('userAuthKeyCloakApi.get:: redirect url --- ', `https://${host}${queryParam}`) res.redirect(redirectUrl) }) diff --git a/src/server.ts b/src/server.ts index c3c018af..380a7e7b 100644 --- a/src/server.ts +++ b/src/server.ts @@ -131,8 +131,6 @@ export class Server { } // tslint:disable-next-line: no-any private setKeyCloak(sessionConfig: any) { - // tslint:disable-next-line: no-console - console.log('setKeyCloak') this.keycloak = new CustomKeycloak(sessionConfig) this.app.use(this.keycloak.middleware) } diff --git a/src/utils/custom-keycloak.ts b/src/utils/custom-keycloak.ts index ece2df68..3c6d29e1 100644 --- a/src/utils/custom-keycloak.ts +++ b/src/utils/custom-keycloak.ts @@ -13,10 +13,6 @@ export class CustomKeycloak { private multiTenantKeycloak = new Map() constructor(sessionConfig: expressSession.SessionOptions) { - // tslint:disable-next-line: no-console - console.log('custom-keycloak::constructor start ') - // tslint:disable-next-line: no-console - console.log('CONSTANTS.MULTI_TENANT_KEYCLOAK -- ', CONSTANTS.MULTI_TENANT_KEYCLOAK) if (CONSTANTS.MULTI_TENANT_KEYCLOAK) { CONSTANTS.MULTI_TENANT_KEYCLOAK.split(';').forEach((v: string) => { const domainUrlMap = v.split(',') @@ -27,8 +23,6 @@ export class CustomKeycloak { }) } this.multiTenantKeycloak.set('common', this.generateKeyCloak(sessionConfig)) - // tslint:disable-next-line: no-console - console.log('custom-keycloak::constructor end ') } middleware = (req: express.Request, res: express.Response, next: express.NextFunction) => { @@ -43,12 +37,6 @@ export class CustomKeycloak { } getKeyCloakObject(req: express.Request): keycloakConnect { - // tslint:disable-next-line: no-console - console.log('custom-keycloak::getKeyCloakObject') - // tslint:disable-next-line: no-console - console.log(`req.header('rootOrg') -- `, req.headers && req.header('rootOrg')) - // tslint:disable-next-line: no-console - console.log(`req.cookies.rootorg -- `, req.cookies && req.cookies.rootorg) const rootOrg = (req.headers ? req.header('rootOrg') : '') || (req.cookies ? req.cookies.rootorg : '') let domain = '' @@ -59,10 +47,6 @@ export class CustomKeycloak { } }) } - // tslint:disable-next-line: no-console - console.log(`custom-keycloak::getKeyCloakObject before retun value -- `, this.multiTenantKeycloak.get(req.hostname) || - this.multiTenantKeycloak.get(domain) || - this.multiTenantKeycloak.get('common')) return (this.multiTenantKeycloak.get(req.hostname) || this.multiTenantKeycloak.get(domain) || @@ -71,23 +55,16 @@ export class CustomKeycloak { // tslint:disable-next-line: no-any authenticated = (request: any, next: any) => { - // tslint:disable-next-line: no-console - console.log('Step 3: authenticated function', '------', new Date()) + logInfo('Step 3: authenticated function', '------', new Date().toString()) try { const userId = request.kauth.grant.access_token.content.sub.split(':') request.session.userId = userId[userId.length - 1] - // tslint:disable-next-line: no-console - console.log('userId ::', userId) - // tslint:disable-next-line: no-console - console.log('request.session after adding userId ::', request.session) } catch (err) { logError('userId conversation error' + request.kauth.grant.access_token.content.sub) } const postLoginRequest = [] // tslint:disable-next-line: no-any postLoginRequest.push((callback: any) => { - // tslint:disable-next-line: no-console - console.log('pushing task to postLoginRequest', '------', new Date()) PERMISSION_HELPER.getCurrentUserRoles(request, callback) }) @@ -112,8 +89,6 @@ export class CustomKeycloak { } protect = (req: express.Request, res: express.Response, next: express.NextFunction) => { - // tslint:disable-next-line: no-console - console.log('custom-keycloak::protect start', '------', new Date()) const keycloak = this.getKeyCloakObject(req) return keycloak.protect()(req, res, next) } diff --git a/src/utils/permissionHelper.ts b/src/utils/permissionHelper.ts index 6b08e13d..e3c3c19e 100644 --- a/src/utils/permissionHelper.ts +++ b/src/utils/permissionHelper.ts @@ -9,7 +9,6 @@ import { extractUserToken } from './requestExtract' export const PERMISSION_HELPER = { // tslint:disable-next-line: no-any setRolesData(reqObj: any, callback: any, body: any) { - logInfo('permission helper:: setRolesData function ', '------', new Date().toString()) // tslint:disable-next-line: no-any const userData: any = JSON.parse(body) logInfo(JSON.stringify(userData)) @@ -30,7 +29,6 @@ export const PERMISSION_HELPER = { logError('reqObj.session.save error -- ', error) callback(error, null) } else { - logInfo('Before calling createNodeBBUser', '------', new Date().toString()) this.createNodeBBUser(reqObj, callback) callback(null, userData) } @@ -42,7 +40,6 @@ export const PERMISSION_HELPER = { }, // tslint:disable-next-line: no-any setNodeBBUID(reqObj: any, callback: any, body: any) { - logInfo('permissionHelper:: setNodeBBUID function start', '------', new Date().toString()) // tslint:disable-next-line: no-any const nodeBBData: any = body if (reqObj.session) { @@ -56,15 +53,11 @@ export const PERMISSION_HELPER = { callback(error, null) } }) - logInfo('permissionHelper:: setNodeBBUID function end', '------', new Date().toString()) }, // tslint:disable-next-line: no-any getCurrentUserRoles(reqObj: any, callback: any) { logInfo('Step 3: Get user roles function', '------', new Date().toString()) const userId = reqObj.session.userId - // tslint:disable-next-line: no-console - console.log(userId) - logInfo('Calling user/v2/read ', '------', new Date().toString()) const readUrl = `${CONSTANTS.KONG_API_BASE}/user/v2/read/` + userId const options = { headers: { @@ -77,24 +70,16 @@ export const PERMISSION_HELPER = { } // tslint:disable-next-line: no-any request.get(options, (err: any, _httpResponse: any, body: any) => { - logInfo('Inside user/v2/read', '------', new Date().toString()) if (body) { - // tslint:disable-next-line: no-console - console.log('------', new Date().toString()) - // tslint:disable-next-line: no-console - console.log('Success user/v2/read: body', body) this.setRolesData(reqObj, callback, body) } if (err) { - // tslint:disable-next-line: no-console - console.log('Making axios call to nodeBB ERROR -- ', err) callback(err, null) } }) }, // tslint:disable-next-line: no-any async createNodeBBUser(reqObj: any, callback: any) { - logInfo('permissionHelper::createNodeBBUser function start', '------', new Date().toString()) const readUrl = `${CONSTANTS.KONG_API_BASE}/discussion/user/v1/create` // tslint:disable-next-line: no-commented-code @@ -104,7 +89,6 @@ export const PERMISSION_HELPER = { identifier: reqObj.session.userId, fullname: reqObj.session.firstName + ' ' + reqObj.session.lastName, } - logInfo('Making axios call to nodeBB', '------', new Date().toString()) try { const nodeBBResp = await axios({ ...axiosRequestConfig, @@ -118,11 +102,8 @@ export const PERMISSION_HELPER = { url: readUrl, }) if (nodeBBResp) { - // tslint:disable-next-line: no-console - console.log('NodeBB axios call response', '------', new Date().toString()) this.setNodeBBUID(reqObj, callback, nodeBBResp) } - logInfo('permissionHelper::createNodeBBUser function end') } catch (err) { // tslint:disable-next-line: no-console console.log('Making axios call to nodeBB ERROR -- ', err) From e415ef2477867ad31cd3f1f54a41d7c6f3a5c0c6 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Tue, 18 Jan 2022 16:05:43 +0530 Subject: [PATCH 231/856] added reviewr and publish --- src/protectedApi_v8/contentprivate.ts | 226 ++++++++++++++++++++++++++ src/protectedApi_v8/protectedApiV8.ts | 2 + 2 files changed, 228 insertions(+) create mode 100644 src/protectedApi_v8/contentprivate.ts diff --git a/src/protectedApi_v8/contentprivate.ts b/src/protectedApi_v8/contentprivate.ts new file mode 100644 index 00000000..6710850e --- /dev/null +++ b/src/protectedApi_v8/contentprivate.ts @@ -0,0 +1,226 @@ +import axios from 'axios' +import { Router } from 'express' + +import { axiosRequestConfig } from '../configs/request.config' +import { CONSTANTS } from '../utils/env' +import { logError } from '../utils/logger' +import { ERROR } from '../utils/message' +import { extractUserId, extractUserToken } from '../utils/requestExtract' + +export const contentPrivateApi = Router() + +const API_END_POINTS = { + getHierarchyDetails: (id: string) => `${CONSTANTS.KNOWLEDGE_MW_API_BASE}/action/content/v3/hierarchy/${id}?mode=edit`, + readUserEndPoint: (userId: string) => `${CONSTANTS.KONG_API_BASE}/user/v2/read/${userId}`, + updateContentEndPoint: (id: string) => `${CONSTANTS.KONG_API_BASE}/private/content/v3/update/${id}`, +} + +const editableFields = ['versionKey', 'createdBy', 'creatorContacts'] +const editableFieldsReviewer = ['versionKey', 'isExternal', 'reviewer', 'reviewerIDs'] +const editableFieldsPublisher = ['versionKey', 'isExternal', 'publisherIDs: ', 'publisherDetails'] +const userIdFailedMessage = 'NO_USER_ID' +const FIELD_VALIDATION_ERROR = 'TRYING_TO_UPDATE_NON_EDITABLE_FIELDS' +const CHANNEL_VALIDATION_ERROR = 'SOURCE_MISMATCH_ERROR' + +contentPrivateApi.patch('/update/:id', async (req, res) => { + try { + const id = req.params.id + const content = req.body.content + const fields = Object.keys(content) + const userId = extractUserId(req) + const userToken = extractUserToken(req) as string + if (!userId) { + res.status(400).send(userIdFailedMessage) + return + } + if (fields instanceof Array) { + for (const entry of fields) { + if (editableFields.indexOf(entry) === -1) { + res.status(400).send({ + msg: res.status(400).send({ + msg: FIELD_VALIDATION_ERROR, + }), + }) + } + } + } + const userChannel = getUserChannel(userToken, userId) + const hierarchySource = getHierarchyDetails(userToken, id) + if (userChannel !== hierarchySource) { + res.status(400).send({ + msg: res.status(400).send({ + msg: CHANNEL_VALIDATION_ERROR, + }), + }) + } + const response = await axios.patch( + API_END_POINTS.updateContentEndPoint(id), + req.body, + { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': userToken, + }, + } + ) + res.status(response.status).send(response.data) + } catch (err) { + logError(Error + err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: ERROR.GENERAL_ERR_MSG, + } + ) + } +}) + +contentPrivateApi.patch('/migratereviewer/:id', async (req, res) => { + try { + const id = req.params.id + const content = req.body.content + const fields = Object.keys(content) + const userId = extractUserId(req) + const userToken = extractUserToken(req) as string + if (!userId) { + res.status(400).send(userIdFailedMessage) + return + } + if (fields instanceof Array) { + for (const entry of fields) { + if (editableFieldsReviewer.indexOf(entry) === -1 && fields.length === editableFieldsReviewer.length ) { + res.status(400).send({ + msg: res.status(400).send({ + msg: FIELD_VALIDATION_ERROR, + }), + }) + } + } + } + const userChannel = getUserChannel(userToken, userId) + const hierarchySource = getHierarchyDetails(userToken, id) + if (userChannel !== hierarchySource) { + res.status(400).send({ + msg: res.status(400).send({ + msg: CHANNEL_VALIDATION_ERROR, + }), + }) + } + const response = await axios.patch( + API_END_POINTS.updateContentEndPoint(id), + req.body, + { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': userToken, + }, + } + ) + res.status(response.status).send(response.data) + } catch (err) { + logError(Error + err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: ERROR.GENERAL_ERR_MSG, + } + ) + } +}) + +contentPrivateApi.patch('/migratepublisher/:id', async (req, res) => { + try { + const id = req.params.id + const content = req.body.content + const fields = Object.keys(content) + const userId = extractUserId(req) + const userToken = extractUserToken(req) as string + if (!userId) { + res.status(400).send(userIdFailedMessage) + return + } + if (fields instanceof Array) { + for (const entry of fields) { + if (editableFieldsPublisher.indexOf(entry) === -1 && fields.length === editableFieldsPublisher.length ) { + res.status(400).send({ + msg: res.status(400).send({ + msg: FIELD_VALIDATION_ERROR, + }), + }) + } + } + } + const userChannel = getUserChannel(userToken, userId) + const hierarchySource = getHierarchyDetails(userToken, id) + if (userChannel !== hierarchySource) { + res.status(400).send({ + msg: res.status(400).send({ + msg: CHANNEL_VALIDATION_ERROR, + }), + }) + } + const response = await axios.patch( + API_END_POINTS.updateContentEndPoint(id), + req.body, + { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': userToken, + }, + } + ) + res.status(response.status).send(response.data) + } catch (err) { + logError(Error + err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: ERROR.GENERAL_ERR_MSG, + } + ) + } +}) + +export async function getHierarchyDetails(token: string, id: string) { + try { + const response = await axios.get(API_END_POINTS.getHierarchyDetails(id), { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': token, + }, + }) + const hierarchyResult = response.data.result.content + if (typeof hierarchyResult !== 'undefined' && hierarchyResult != null) { + return hierarchyResult.source + } + } catch (error) { + logError('ERROR WHILE FETCHING THE Hierarchy DETAILS --> ', error) + return 'contentSourceDetails' + } +} + +export async function getUserChannel(token: string, userId: string) { + try { + const response = await axios.get(API_END_POINTS.readUserEndPoint(userId), { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': token, + }, + }) + const userProfileResult = response.data.result.response + if (typeof userProfileResult !== 'undefined' && userProfileResult != null) { + return userProfileResult.channel + } + } catch (error) { + logError('ERROR WHILE FETCHING THE USER DETAILS --> ', error) + return 'userChannelDetails' + } +} + \ No newline at end of file diff --git a/src/protectedApi_v8/protectedApiV8.ts b/src/protectedApi_v8/protectedApiV8.ts index 9f92a395..1bbb53e0 100644 --- a/src/protectedApi_v8/protectedApiV8.ts +++ b/src/protectedApi_v8/protectedApiV8.ts @@ -11,6 +11,7 @@ import { conceptGraphApi } from './concept' // import { connectionsApi } from './connections' import { connectionsV2Api } from './connections_v2' import { contentApi } from './content' +import { contentPrivateApi } from './contentprivate' import { contentValidationApi } from './contentValidation' import { counterApi } from './counter' import { deptApi } from './departments' @@ -79,3 +80,4 @@ protectedApiV8.use('/roleactivity', roleActivityApi) protectedApiV8.use('/resource', userAuthKeyCloakApi) protectedApiV8.use('/workallocation', workAllocationApi) protectedApiV8.use('/frac', fracApi) +protectedApiV8.use('/contentprivate', contentPrivateApi) From daa15e82221eacb0d29ac3f9909f2c6a3e8ca0d6 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Tue, 18 Jan 2022 16:32:20 +0530 Subject: [PATCH 232/856] added rolcheck for update reviewer and publisher api --- src/utils/whitelistApis.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 6efd78c5..e28eaaa4 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1295,6 +1295,27 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + 'protected/v8/contentprivate/update/:do_id':{ + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.CONTENT_CREATOR, + ], + }, + 'protected/v8/contentprivate/migratepublisher/:do_id':{ + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.CONTENT_CREATOR, + ], + }, + 'protected/v8/contentprivate/migratereviewer/:do_id':{ + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.CONTENT_CREATOR, + ], + }, '/protected/v8/connections/v2/connections/established/:id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1664,6 +1685,9 @@ export const API_LIST = { '/protected/v8/workallocation/getWOPdf/:workOrderId', '/protected/v8/portal/cbp/mydepartment', '/protected/v8/user/mandatoryContent/checkStatus', + 'protected/v8/contentprivate/update/:do_id', + 'protected/v8/contentprivate/migratepublisher/:do_id', + 'protected/v8/contentprivate/migratereviewer/:do_id', '/protected/v8/user/rating/content/average-ratingInfo/:do_id', '/protected/v8/social/post/timeline', '/protected/v8/user/history/:id', From 9af1f3ac1f8a289bef2c0f15bf3fb57a32550aac Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Tue, 18 Jan 2022 16:37:00 +0530 Subject: [PATCH 233/856] added / --- src/utils/whitelistApis.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index e28eaaa4..d2760f60 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1295,21 +1295,21 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - 'protected/v8/contentprivate/update/:do_id':{ + '/protected/v8/contentprivate/update/:do_id':{ checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.CONTENT_CREATOR, ], }, - 'protected/v8/contentprivate/migratepublisher/:do_id':{ + '/protected/v8/contentprivate/migratepublisher/:do_id':{ checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.CONTENT_CREATOR, ], }, - 'protected/v8/contentprivate/migratereviewer/:do_id':{ + '/protected/v8/contentprivate/migratereviewer/:do_id':{ checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -1685,9 +1685,9 @@ export const API_LIST = { '/protected/v8/workallocation/getWOPdf/:workOrderId', '/protected/v8/portal/cbp/mydepartment', '/protected/v8/user/mandatoryContent/checkStatus', - 'protected/v8/contentprivate/update/:do_id', - 'protected/v8/contentprivate/migratepublisher/:do_id', - 'protected/v8/contentprivate/migratereviewer/:do_id', + '/protected/v8/contentprivate/update/:do_id', + '/protected/v8/contentprivate/migratepublisher/:do_id', + '/protected/v8/contentprivate/migratereviewer/:do_id', '/protected/v8/user/rating/content/average-ratingInfo/:do_id', '/protected/v8/social/post/timeline', '/protected/v8/user/history/:id', From c991c4917cbc98bdc3a2c3247a406aeb3acfdd6f Mon Sep 17 00:00:00 2001 From: "christopher.fernandes" Date: Tue, 18 Jan 2022 18:24:58 +0530 Subject: [PATCH 234/856] Task #0000: changed the callback to trigger after nodebb calls --- src/utils/custom-keycloak.ts | 25 +++++++++++++++++++++++++ src/utils/permissionHelper.ts | 21 +++++++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/utils/custom-keycloak.ts b/src/utils/custom-keycloak.ts index 3c6d29e1..72450830 100644 --- a/src/utils/custom-keycloak.ts +++ b/src/utils/custom-keycloak.ts @@ -13,6 +13,10 @@ export class CustomKeycloak { private multiTenantKeycloak = new Map() constructor(sessionConfig: expressSession.SessionOptions) { + // tslint:disable-next-line: no-console + console.log('custom-keycloak::constructor start ') + // tslint:disable-next-line: no-console + console.log('CONSTANTS.MULTI_TENANT_KEYCLOAK -- ', CONSTANTS.MULTI_TENANT_KEYCLOAK) if (CONSTANTS.MULTI_TENANT_KEYCLOAK) { CONSTANTS.MULTI_TENANT_KEYCLOAK.split(';').forEach((v: string) => { const domainUrlMap = v.split(',') @@ -23,6 +27,8 @@ export class CustomKeycloak { }) } this.multiTenantKeycloak.set('common', this.generateKeyCloak(sessionConfig)) + // tslint:disable-next-line: no-console + console.log('custom-keycloak::constructor end ') } middleware = (req: express.Request, res: express.Response, next: express.NextFunction) => { @@ -37,6 +43,12 @@ export class CustomKeycloak { } getKeyCloakObject(req: express.Request): keycloakConnect { + // tslint:disable-next-line: no-console + console.log('custom-keycloak::getKeyCloakObject') + // tslint:disable-next-line: no-console + console.log(`req.header('rootOrg') -- `, req.headers && req.header('rootOrg')) + // tslint:disable-next-line: no-console + console.log(`req.cookies.rootorg -- `, req.cookies && req.cookies.rootorg) const rootOrg = (req.headers ? req.header('rootOrg') : '') || (req.cookies ? req.cookies.rootorg : '') let domain = '' @@ -48,6 +60,11 @@ export class CustomKeycloak { }) } + // tslint:disable-next-line: no-console + console.log(`custom-keycloak::getKeyCloakObject before retun value -- `, this.multiTenantKeycloak.get(req.hostname) || + this.multiTenantKeycloak.get(domain) || + this.multiTenantKeycloak.get('common')) + return (this.multiTenantKeycloak.get(req.hostname) || this.multiTenantKeycloak.get(domain) || this.multiTenantKeycloak.get('common')) as keycloakConnect @@ -59,12 +76,18 @@ export class CustomKeycloak { try { const userId = request.kauth.grant.access_token.content.sub.split(':') request.session.userId = userId[userId.length - 1] + // tslint:disable-next-line: no-console + console.log('userId ::', userId) + // tslint:disable-next-line: no-console + console.log('request.session after adding userId ::', request.session) } catch (err) { logError('userId conversation error' + request.kauth.grant.access_token.content.sub) } const postLoginRequest = [] // tslint:disable-next-line: no-any postLoginRequest.push((callback: any) => { + // tslint:disable-next-line: no-console + console.log('pushing task to postLoginRequest', '------', new Date()) PERMISSION_HELPER.getCurrentUserRoles(request, callback) }) @@ -89,6 +112,8 @@ export class CustomKeycloak { } protect = (req: express.Request, res: express.Response, next: express.NextFunction) => { + // tslint:disable-next-line: no-console + console.log('custom-keycloak::protect start', '------', new Date()) const keycloak = this.getKeyCloakObject(req) return keycloak.protect()(req, res, next) } diff --git a/src/utils/permissionHelper.ts b/src/utils/permissionHelper.ts index e3c3c19e..eb67fdb0 100644 --- a/src/utils/permissionHelper.ts +++ b/src/utils/permissionHelper.ts @@ -9,6 +9,7 @@ import { extractUserToken } from './requestExtract' export const PERMISSION_HELPER = { // tslint:disable-next-line: no-any setRolesData(reqObj: any, callback: any, body: any) { + logInfo('permission helper:: setRolesData function ', '------', new Date().toString()) // tslint:disable-next-line: no-any const userData: any = JSON.parse(body) logInfo(JSON.stringify(userData)) @@ -29,8 +30,9 @@ export const PERMISSION_HELPER = { logError('reqObj.session.save error -- ', error) callback(error, null) } else { + logInfo('Before calling createNodeBBUser', '------', new Date().toString()) this.createNodeBBUser(reqObj, callback) - callback(null, userData) + // callback(null, userData) } }) } else { @@ -40,6 +42,7 @@ export const PERMISSION_HELPER = { }, // tslint:disable-next-line: no-any setNodeBBUID(reqObj: any, callback: any, body: any) { + logInfo('permissionHelper:: setNodeBBUID function start', '------', new Date().toString()) // tslint:disable-next-line: no-any const nodeBBData: any = body if (reqObj.session) { @@ -51,8 +54,11 @@ export const PERMISSION_HELPER = { if (error) { logError('reqObj.session.save error -- ', error) callback(error, null) - } + } else { + callback(null, nodeBBData) + } }) + logInfo('permissionHelper:: setNodeBBUID function end', '------', new Date().toString()) }, // tslint:disable-next-line: no-any getCurrentUserRoles(reqObj: any, callback: any) { @@ -70,10 +76,17 @@ export const PERMISSION_HELPER = { } // tslint:disable-next-line: no-any request.get(options, (err: any, _httpResponse: any, body: any) => { + logInfo('Inside user/v2/read', '------', new Date().toString()) if (body) { + // tslint:disable-next-line: no-console + console.log('------', new Date().toString()) + // tslint:disable-next-line: no-console + console.log('Success user/v2/read: body', body) this.setRolesData(reqObj, callback, body) } if (err) { + // tslint:disable-next-line: no-console + console.log('Making axios call to nodeBB ERROR -- ', err) callback(err, null) } }) @@ -89,6 +102,7 @@ export const PERMISSION_HELPER = { identifier: reqObj.session.userId, fullname: reqObj.session.firstName + ' ' + reqObj.session.lastName, } + logInfo('Making axios call to nodeBB', '------', new Date().toString()) try { const nodeBBResp = await axios({ ...axiosRequestConfig, @@ -102,8 +116,11 @@ export const PERMISSION_HELPER = { url: readUrl, }) if (nodeBBResp) { + // tslint:disable-next-line: no-console + console.log('NodeBB axios call response', '------', new Date().toString()) this.setNodeBBUID(reqObj, callback, nodeBBResp) } + logInfo('permissionHelper::createNodeBBUser function end') } catch (err) { // tslint:disable-next-line: no-console console.log('Making axios call to nodeBB ERROR -- ', err) From 974f5c45cface79fbfdcf6faee00847a351cf6c2 Mon Sep 17 00:00:00 2001 From: "christopher.fernandes" Date: Wed, 19 Jan 2022 11:47:13 +0530 Subject: [PATCH 235/856] Task #0000: Removed session save, added cookie logs --- src/protectedApi_v8/contentprivate.ts | 451 +++++++++++++------------- src/server.ts | 2 +- src/utils/apiWhiteList.ts | 5 +- src/utils/custom-keycloak.ts | 12 +- src/utils/permissionHelper.ts | 25 +- src/utils/proxyCreator.ts | 9 +- src/utils/whitelistApis.ts | 6 +- 7 files changed, 254 insertions(+), 256 deletions(-) diff --git a/src/protectedApi_v8/contentprivate.ts b/src/protectedApi_v8/contentprivate.ts index 6710850e..ff1fb28a 100644 --- a/src/protectedApi_v8/contentprivate.ts +++ b/src/protectedApi_v8/contentprivate.ts @@ -1,226 +1,225 @@ -import axios from 'axios' -import { Router } from 'express' - -import { axiosRequestConfig } from '../configs/request.config' -import { CONSTANTS } from '../utils/env' -import { logError } from '../utils/logger' -import { ERROR } from '../utils/message' -import { extractUserId, extractUserToken } from '../utils/requestExtract' - -export const contentPrivateApi = Router() - -const API_END_POINTS = { - getHierarchyDetails: (id: string) => `${CONSTANTS.KNOWLEDGE_MW_API_BASE}/action/content/v3/hierarchy/${id}?mode=edit`, - readUserEndPoint: (userId: string) => `${CONSTANTS.KONG_API_BASE}/user/v2/read/${userId}`, - updateContentEndPoint: (id: string) => `${CONSTANTS.KONG_API_BASE}/private/content/v3/update/${id}`, -} - -const editableFields = ['versionKey', 'createdBy', 'creatorContacts'] -const editableFieldsReviewer = ['versionKey', 'isExternal', 'reviewer', 'reviewerIDs'] -const editableFieldsPublisher = ['versionKey', 'isExternal', 'publisherIDs: ', 'publisherDetails'] -const userIdFailedMessage = 'NO_USER_ID' -const FIELD_VALIDATION_ERROR = 'TRYING_TO_UPDATE_NON_EDITABLE_FIELDS' -const CHANNEL_VALIDATION_ERROR = 'SOURCE_MISMATCH_ERROR' - -contentPrivateApi.patch('/update/:id', async (req, res) => { - try { - const id = req.params.id - const content = req.body.content - const fields = Object.keys(content) - const userId = extractUserId(req) - const userToken = extractUserToken(req) as string - if (!userId) { - res.status(400).send(userIdFailedMessage) - return - } - if (fields instanceof Array) { - for (const entry of fields) { - if (editableFields.indexOf(entry) === -1) { - res.status(400).send({ - msg: res.status(400).send({ - msg: FIELD_VALIDATION_ERROR, - }), - }) - } - } - } - const userChannel = getUserChannel(userToken, userId) - const hierarchySource = getHierarchyDetails(userToken, id) - if (userChannel !== hierarchySource) { - res.status(400).send({ - msg: res.status(400).send({ - msg: CHANNEL_VALIDATION_ERROR, - }), - }) - } - const response = await axios.patch( - API_END_POINTS.updateContentEndPoint(id), - req.body, - { - ...axiosRequestConfig, - headers: { - Authorization: CONSTANTS.SB_API_KEY, - // tslint:disable-next-line: all - 'x-authenticated-user-token': userToken, - }, - } - ) - res.status(response.status).send(response.data) - } catch (err) { - logError(Error + err) - res.status((err && err.response && err.response.status) || 500).send( - (err && err.response && err.response.data) || { - error: ERROR.GENERAL_ERR_MSG, - } - ) - } -}) - -contentPrivateApi.patch('/migratereviewer/:id', async (req, res) => { - try { - const id = req.params.id - const content = req.body.content - const fields = Object.keys(content) - const userId = extractUserId(req) - const userToken = extractUserToken(req) as string - if (!userId) { - res.status(400).send(userIdFailedMessage) - return - } - if (fields instanceof Array) { - for (const entry of fields) { - if (editableFieldsReviewer.indexOf(entry) === -1 && fields.length === editableFieldsReviewer.length ) { - res.status(400).send({ - msg: res.status(400).send({ - msg: FIELD_VALIDATION_ERROR, - }), - }) - } - } - } - const userChannel = getUserChannel(userToken, userId) - const hierarchySource = getHierarchyDetails(userToken, id) - if (userChannel !== hierarchySource) { - res.status(400).send({ - msg: res.status(400).send({ - msg: CHANNEL_VALIDATION_ERROR, - }), - }) - } - const response = await axios.patch( - API_END_POINTS.updateContentEndPoint(id), - req.body, - { - ...axiosRequestConfig, - headers: { - Authorization: CONSTANTS.SB_API_KEY, - // tslint:disable-next-line: all - 'x-authenticated-user-token': userToken, - }, - } - ) - res.status(response.status).send(response.data) - } catch (err) { - logError(Error + err) - res.status((err && err.response && err.response.status) || 500).send( - (err && err.response && err.response.data) || { - error: ERROR.GENERAL_ERR_MSG, - } - ) - } -}) - -contentPrivateApi.patch('/migratepublisher/:id', async (req, res) => { - try { - const id = req.params.id - const content = req.body.content - const fields = Object.keys(content) - const userId = extractUserId(req) - const userToken = extractUserToken(req) as string - if (!userId) { - res.status(400).send(userIdFailedMessage) - return - } - if (fields instanceof Array) { - for (const entry of fields) { - if (editableFieldsPublisher.indexOf(entry) === -1 && fields.length === editableFieldsPublisher.length ) { - res.status(400).send({ - msg: res.status(400).send({ - msg: FIELD_VALIDATION_ERROR, - }), - }) - } - } - } - const userChannel = getUserChannel(userToken, userId) - const hierarchySource = getHierarchyDetails(userToken, id) - if (userChannel !== hierarchySource) { - res.status(400).send({ - msg: res.status(400).send({ - msg: CHANNEL_VALIDATION_ERROR, - }), - }) - } - const response = await axios.patch( - API_END_POINTS.updateContentEndPoint(id), - req.body, - { - ...axiosRequestConfig, - headers: { - Authorization: CONSTANTS.SB_API_KEY, - // tslint:disable-next-line: all - 'x-authenticated-user-token': userToken, - }, - } - ) - res.status(response.status).send(response.data) - } catch (err) { - logError(Error + err) - res.status((err && err.response && err.response.status) || 500).send( - (err && err.response && err.response.data) || { - error: ERROR.GENERAL_ERR_MSG, - } - ) - } -}) - -export async function getHierarchyDetails(token: string, id: string) { - try { - const response = await axios.get(API_END_POINTS.getHierarchyDetails(id), { - ...axiosRequestConfig, - headers: { - Authorization: CONSTANTS.SB_API_KEY, - // tslint:disable-next-line: all - 'x-authenticated-user-token': token, - }, - }) - const hierarchyResult = response.data.result.content - if (typeof hierarchyResult !== 'undefined' && hierarchyResult != null) { - return hierarchyResult.source - } - } catch (error) { - logError('ERROR WHILE FETCHING THE Hierarchy DETAILS --> ', error) - return 'contentSourceDetails' - } -} - -export async function getUserChannel(token: string, userId: string) { - try { - const response = await axios.get(API_END_POINTS.readUserEndPoint(userId), { - ...axiosRequestConfig, - headers: { - Authorization: CONSTANTS.SB_API_KEY, - // tslint:disable-next-line: all - 'x-authenticated-user-token': token, - }, - }) - const userProfileResult = response.data.result.response - if (typeof userProfileResult !== 'undefined' && userProfileResult != null) { - return userProfileResult.channel - } - } catch (error) { - logError('ERROR WHILE FETCHING THE USER DETAILS --> ', error) - return 'userChannelDetails' - } -} - \ No newline at end of file +import axios from 'axios' +import { Router } from 'express' + +import { axiosRequestConfig } from '../configs/request.config' +import { CONSTANTS } from '../utils/env' +import { logError } from '../utils/logger' +import { ERROR } from '../utils/message' +import { extractUserId, extractUserToken } from '../utils/requestExtract' + +export const contentPrivateApi = Router() + +const API_END_POINTS = { + getHierarchyDetails: (id: string) => `${CONSTANTS.KNOWLEDGE_MW_API_BASE}/action/content/v3/hierarchy/${id}?mode=edit`, + readUserEndPoint: (userId: string) => `${CONSTANTS.KONG_API_BASE}/user/v2/read/${userId}`, + updateContentEndPoint: (id: string) => `${CONSTANTS.KONG_API_BASE}/private/content/v3/update/${id}`, +} + +const editableFields = ['versionKey', 'createdBy', 'creatorContacts'] +const editableFieldsReviewer = ['versionKey', 'isExternal', 'reviewer', 'reviewerIDs'] +const editableFieldsPublisher = ['versionKey', 'isExternal', 'publisherIDs: ', 'publisherDetails'] +const userIdFailedMessage = 'NO_USER_ID' +const FIELD_VALIDATION_ERROR = 'TRYING_TO_UPDATE_NON_EDITABLE_FIELDS' +const CHANNEL_VALIDATION_ERROR = 'SOURCE_MISMATCH_ERROR' + +contentPrivateApi.patch('/update/:id', async (req, res) => { + try { + const id = req.params.id + const content = req.body.content + const fields = Object.keys(content) + const userId = extractUserId(req) + const userToken = extractUserToken(req) as string + if (!userId) { + res.status(400).send(userIdFailedMessage) + return + } + if (fields instanceof Array) { + for (const entry of fields) { + if (editableFields.indexOf(entry) === -1) { + res.status(400).send({ + msg: res.status(400).send({ + msg: FIELD_VALIDATION_ERROR, + }), + }) + } + } + } + const userChannel = getUserChannel(userToken, userId) + const hierarchySource = getHierarchyDetails(userToken, id) + if (userChannel !== hierarchySource) { + res.status(400).send({ + msg: res.status(400).send({ + msg: CHANNEL_VALIDATION_ERROR, + }), + }) + } + const response = await axios.patch( + API_END_POINTS.updateContentEndPoint(id), + req.body, + { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': userToken, + }, + } + ) + res.status(response.status).send(response.data) + } catch (err) { + logError(Error + err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: ERROR.GENERAL_ERR_MSG, + } + ) + } +}) + +contentPrivateApi.patch('/migratereviewer/:id', async (req, res) => { + try { + const id = req.params.id + const content = req.body.content + const fields = Object.keys(content) + const userId = extractUserId(req) + const userToken = extractUserToken(req) as string + if (!userId) { + res.status(400).send(userIdFailedMessage) + return + } + if (fields instanceof Array) { + for (const entry of fields) { + if (editableFieldsReviewer.indexOf(entry) === -1 && fields.length === editableFieldsReviewer.length ) { + res.status(400).send({ + msg: res.status(400).send({ + msg: FIELD_VALIDATION_ERROR, + }), + }) + } + } + } + const userChannel = getUserChannel(userToken, userId) + const hierarchySource = getHierarchyDetails(userToken, id) + if (userChannel !== hierarchySource) { + res.status(400).send({ + msg: res.status(400).send({ + msg: CHANNEL_VALIDATION_ERROR, + }), + }) + } + const response = await axios.patch( + API_END_POINTS.updateContentEndPoint(id), + req.body, + { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': userToken, + }, + } + ) + res.status(response.status).send(response.data) + } catch (err) { + logError(Error + err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: ERROR.GENERAL_ERR_MSG, + } + ) + } +}) + +contentPrivateApi.patch('/migratepublisher/:id', async (req, res) => { + try { + const id = req.params.id + const content = req.body.content + const fields = Object.keys(content) + const userId = extractUserId(req) + const userToken = extractUserToken(req) as string + if (!userId) { + res.status(400).send(userIdFailedMessage) + return + } + if (fields instanceof Array) { + for (const entry of fields) { + if (editableFieldsPublisher.indexOf(entry) === -1 && fields.length === editableFieldsPublisher.length ) { + res.status(400).send({ + msg: res.status(400).send({ + msg: FIELD_VALIDATION_ERROR, + }), + }) + } + } + } + const userChannel = getUserChannel(userToken, userId) + const hierarchySource = getHierarchyDetails(userToken, id) + if (userChannel !== hierarchySource) { + res.status(400).send({ + msg: res.status(400).send({ + msg: CHANNEL_VALIDATION_ERROR, + }), + }) + } + const response = await axios.patch( + API_END_POINTS.updateContentEndPoint(id), + req.body, + { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': userToken, + }, + } + ) + res.status(response.status).send(response.data) + } catch (err) { + logError(Error + err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: ERROR.GENERAL_ERR_MSG, + } + ) + } +}) + +export async function getHierarchyDetails(token: string, id: string) { + try { + const response = await axios.get(API_END_POINTS.getHierarchyDetails(id), { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': token, + }, + }) + const hierarchyResult = response.data.result.content + if (typeof hierarchyResult !== 'undefined' && hierarchyResult != null) { + return hierarchyResult.source + } + } catch (error) { + logError('ERROR WHILE FETCHING THE Hierarchy DETAILS --> ', error) + return 'contentSourceDetails' + } +} + +export async function getUserChannel(token: string, userId: string) { + try { + const response = await axios.get(API_END_POINTS.readUserEndPoint(userId), { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': token, + }, + }) + const userProfileResult = response.data.result.response + if (typeof userProfileResult !== 'undefined' && userProfileResult != null) { + return userProfileResult.channel + } + } catch (error) { + logError('ERROR WHILE FETCHING THE USER DETAILS --> ', error) + return 'userChannelDetails' + } +} diff --git a/src/server.ts b/src/server.ts index 380a7e7b..8b325748 100644 --- a/src/server.ts +++ b/src/server.ts @@ -48,11 +48,11 @@ export class Server { this.app.use(expressSession(sessionConfig)) this.app.use(express.urlencoded({ extended: false, limit: '50mb' })) this.app.use(express.json({ limit: '50mb' })) + this.setCookie() this.app.all('*', apiWhiteListLogger()) if (CONSTANTS.PORTAL_API_WHITELIST_CHECK === 'true') { this.app.all('*', isAllowed()) } - this.setCookie() this.setKeyCloak(sessionConfig) this.authoringProxies() this.configureMiddleware() diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index 27087c6d..cba9230d 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -365,7 +365,7 @@ const validateAPI = (req: Request, res: Response, next: NextFunction) => { export function apiWhiteListLogger() { return (req: Request, res: Response, next: NextFunction) => { // tslint:disable-next-line: no-console - console.log('req.query:: ', req.query) + console.log('apiWhiteListLogger:: req.query:: ', req.query, '-----cookies---- ', req.cookies) if (req.path === '/' || checkIsStaticRoute(req.path)) { next() return @@ -373,7 +373,8 @@ export function apiWhiteListLogger() { const REQ_URL = req.path if (!_.includes(REQ_URL, '/resource') && (req.session)) { if (!('userRoles' in req.session) || (('userRoles' in req.session) && (req.session.userRoles.length === 0))) { - // console.log('Session not there: In If') + // tslint:disable-next-line: no-console + console.log('apiWhiteListLogger :: req.session', req.session, '----cookie---', req.cookies) logError('Portal_API_WHITELIST_LOGGER: User needs to authenticated themselves') respond419(req, res) } else { diff --git a/src/utils/custom-keycloak.ts b/src/utils/custom-keycloak.ts index 72450830..932b7a55 100644 --- a/src/utils/custom-keycloak.ts +++ b/src/utils/custom-keycloak.ts @@ -43,8 +43,6 @@ export class CustomKeycloak { } getKeyCloakObject(req: express.Request): keycloakConnect { - // tslint:disable-next-line: no-console - console.log('custom-keycloak::getKeyCloakObject') // tslint:disable-next-line: no-console console.log(`req.header('rootOrg') -- `, req.headers && req.header('rootOrg')) // tslint:disable-next-line: no-console @@ -60,11 +58,6 @@ export class CustomKeycloak { }) } - // tslint:disable-next-line: no-console - console.log(`custom-keycloak::getKeyCloakObject before retun value -- `, this.multiTenantKeycloak.get(req.hostname) || - this.multiTenantKeycloak.get(domain) || - this.multiTenantKeycloak.get('common')) - return (this.multiTenantKeycloak.get(req.hostname) || this.multiTenantKeycloak.get(domain) || this.multiTenantKeycloak.get('common')) as keycloakConnect @@ -79,7 +72,8 @@ export class CustomKeycloak { // tslint:disable-next-line: no-console console.log('userId ::', userId) // tslint:disable-next-line: no-console - console.log('request.session after adding userId ::', request.session) + console.log('request.session after adding userId ::', request.session, '----cookie---', request.cookies) + } catch (err) { logError('userId conversation error' + request.kauth.grant.access_token.content.sub) } @@ -92,7 +86,7 @@ export class CustomKeycloak { }) // tslint:disable-next-line: no-any - return async.series(postLoginRequest, (err: any) => { + async.series(postLoginRequest, (err: any) => { if (err) { logError('error loggin in user') next(err, null) diff --git a/src/utils/permissionHelper.ts b/src/utils/permissionHelper.ts index eb67fdb0..5f597806 100644 --- a/src/utils/permissionHelper.ts +++ b/src/utils/permissionHelper.ts @@ -24,17 +24,20 @@ export const PERMISSION_HELPER = { if (!_.includes(reqObj.session.userRoles, 'PUBLIC')) { reqObj.session.userRoles.push('PUBLIC') } + // tslint:disable-next-line: no-console + console.log('request.session after adding userId ::', reqObj.session, '----cookie---', reqObj.cookies) + this.createNodeBBUser(reqObj, callback) // tslint:disable-next-line: no-any - reqObj.session.save((error: any) => { - if (error) { - logError('reqObj.session.save error -- ', error) - callback(error, null) - } else { - logInfo('Before calling createNodeBBUser', '------', new Date().toString()) - this.createNodeBBUser(reqObj, callback) - // callback(null, userData) - } - }) + // reqObj.session.save((error: any) => { + // if (error) { + // logError('reqObj.session.save error -- ', error) + // callback(error, null) + // } else { + // logInfo('Before calling createNodeBBUser', '------', new Date().toString()) + // this.createNodeBBUser(reqObj, callback) + // // callback(null, userData) + // } + // }) } else { callback('reqObj.session no session', null) } @@ -45,6 +48,8 @@ export const PERMISSION_HELPER = { logInfo('permissionHelper:: setNodeBBUID function start', '------', new Date().toString()) // tslint:disable-next-line: no-any const nodeBBData: any = body + // tslint:disable-next-line: no-console + console.log('request.session after adding userId ::', reqObj.session, '----cookie---', reqObj.cookies) if (reqObj.session) { reqObj.session.uid = nodeBBData.data.result.userId.uid logInfo('After appending uid to session', reqObj.session.uid) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 41b1b170..94b848d8 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -15,6 +15,8 @@ const PROXY_SLUG_WAT = '/proxies/v8/wat' // tslint:disable-next-line: no-any proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { + // tslint:disable-next-line: no-console + console.log('proxyReq::req.cookies=====>', req.cookies) proxyReq.setHeader('X-Channel-Id', (_.get(req, 'session.rootOrgId')) ? _.get(req, 'session.rootOrgId') : CONSTANTS.X_Channel_Id) // tslint:disable-next-line: max-line-length proxyReq.setHeader('Authorization', CONSTANTS.SB_API_KEY) @@ -216,11 +218,6 @@ export function proxyCreatorSunbirdSearch(route: Router, targetUrl: string, _tim } export function proxyCreatorToAppentUserId(route: Router, targetUrl: string, _timeout = 10000): Router { - logInfo('proxyCreatorToAppentUserId Function ----') - // tslint:disable-next-line: no-console - console.log('proxyCreatorToAppentUserId Function:: route ----', route) - // tslint:disable-next-line: no-console - console.log('proxyCreatorToAppentUserId Function:: targetUrl ----', targetUrl) route.all('/*', (req, res) => { const originalUrl = req.originalUrl const lastIndex = originalUrl.lastIndexOf('/') @@ -231,6 +228,8 @@ export function proxyCreatorToAppentUserId(route: Router, targetUrl: string, _ti } // tslint:disable-next-line: no-console console.log('REQ_URL_ORIGINAL proxyCreatorToAppentUserId', req.originalUrl) + // tslint:disable-next-line: no-console + console.log('REQ_URL_ORIGINAL -- cookies-- ', req.cookies) proxy.web(req, res, { changeOrigin: true, diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index d2760f60..6a54f20d 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1295,21 +1295,21 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/protected/v8/contentprivate/update/:do_id':{ + '/protected/v8/contentprivate/update/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.CONTENT_CREATOR, ], }, - '/protected/v8/contentprivate/migratepublisher/:do_id':{ + '/protected/v8/contentprivate/migratepublisher/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.CONTENT_CREATOR, ], }, - '/protected/v8/contentprivate/migratereviewer/:do_id':{ + '/protected/v8/contentprivate/migratereviewer/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ From 108683b81c16fe575243b1b75ba1d6aec8143f57 Mon Sep 17 00:00:00 2001 From: "christopher.fernandes" Date: Wed, 19 Jan 2022 16:34:30 +0530 Subject: [PATCH 236/856] Task #0000: Added consistency and more logs --- src/configs/session.config.ts | 10 ++++++++++ src/utils/apiWhiteList.ts | 9 +++++---- src/utils/custom-keycloak.ts | 27 ++++++++++----------------- src/utils/env.ts | 1 + src/utils/permissionHelper.ts | 24 +++++++++++++----------- 5 files changed, 39 insertions(+), 32 deletions(-) diff --git a/src/configs/session.config.ts b/src/configs/session.config.ts index 55589aea..971338f5 100644 --- a/src/configs/session.config.ts +++ b/src/configs/session.config.ts @@ -2,13 +2,17 @@ import cassandraDriver from 'cassandra-driver' import cassandraStore from 'cassandra-store' import expressSession from 'express-session' import { CONSTANTS } from '../utils/env' +const expressCassandra = require('express-cassandra') +const _ = require('lodash') let sessionConfig: expressSession.SessionOptions +const consistency = getConsistencyLevel(CONSTANTS.PORTAL_CASSANDRA_CONSISTENCY_LEVEL) const cassandraClientOptions: cassandraDriver.ClientOptions = { contactPoints: getIPList(), keyspace: 'portal', queryOptions: { + consistency, prepare: true, }, } @@ -50,3 +54,9 @@ export function getSessionConfig( } return sessionConfig } + +// tslint:disable-next-line: no-any +function getConsistencyLevel(consistencyParam: any) { + // tslint:disable-next-line: max-line-length + return (consistencyParam && _.get(expressCassandra, `consistencies.${consistencyParam}`) ? _.get(expressCassandra, `consistencies.${consistencyParam}`) : expressCassandra.consistencies.one) +} diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index cba9230d..ce1bfdf8 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -365,7 +365,8 @@ const validateAPI = (req: Request, res: Response, next: NextFunction) => { export function apiWhiteListLogger() { return (req: Request, res: Response, next: NextFunction) => { // tslint:disable-next-line: no-console - console.log('apiWhiteListLogger:: req.query:: ', req.query, '-----cookies---- ', req.cookies) + console.log('apiWhiteListLogger::req.path', req.path , '-- req.query:: ', req.query, '-----cookies---- ', req.cookies, + '------', new Date().toString()) if (req.path === '/' || checkIsStaticRoute(req.path)) { next() return @@ -374,12 +375,12 @@ export function apiWhiteListLogger() { if (!_.includes(REQ_URL, '/resource') && (req.session)) { if (!('userRoles' in req.session) || (('userRoles' in req.session) && (req.session.userRoles.length === 0))) { // tslint:disable-next-line: no-console - console.log('apiWhiteListLogger :: req.session', req.session, '----cookie---', req.cookies) - logError('Portal_API_WHITELIST_LOGGER: User needs to authenticated themselves') + console.log('apiWhiteListLogger :: req.session', req.session, '----cookie---', req.cookies, '------', new Date().toString()) + logError('Portal_API_WHITELIST_LOGGER: User needs to authenticated themselves', '------', new Date().toString()) respond419(req, res) } else { // Pattern match for URL - logInfo('In WhilteList Call========' + REQ_URL) + logInfo('In WhilteList Call========' + REQ_URL, '------', new Date().toString()) validateAPI(req, res, next) } } else { diff --git a/src/utils/custom-keycloak.ts b/src/utils/custom-keycloak.ts index 932b7a55..84b40d1f 100644 --- a/src/utils/custom-keycloak.ts +++ b/src/utils/custom-keycloak.ts @@ -13,10 +13,6 @@ export class CustomKeycloak { private multiTenantKeycloak = new Map() constructor(sessionConfig: expressSession.SessionOptions) { - // tslint:disable-next-line: no-console - console.log('custom-keycloak::constructor start ') - // tslint:disable-next-line: no-console - console.log('CONSTANTS.MULTI_TENANT_KEYCLOAK -- ', CONSTANTS.MULTI_TENANT_KEYCLOAK) if (CONSTANTS.MULTI_TENANT_KEYCLOAK) { CONSTANTS.MULTI_TENANT_KEYCLOAK.split(';').forEach((v: string) => { const domainUrlMap = v.split(',') @@ -27,8 +23,6 @@ export class CustomKeycloak { }) } this.multiTenantKeycloak.set('common', this.generateKeyCloak(sessionConfig)) - // tslint:disable-next-line: no-console - console.log('custom-keycloak::constructor end ') } middleware = (req: express.Request, res: express.Response, next: express.NextFunction) => { @@ -43,10 +37,6 @@ export class CustomKeycloak { } getKeyCloakObject(req: express.Request): keycloakConnect { - // tslint:disable-next-line: no-console - console.log(`req.header('rootOrg') -- `, req.headers && req.header('rootOrg')) - // tslint:disable-next-line: no-console - console.log(`req.cookies.rootorg -- `, req.cookies && req.cookies.rootorg) const rootOrg = (req.headers ? req.header('rootOrg') : '') || (req.cookies ? req.cookies.rootorg : '') let domain = '' @@ -70,28 +60,31 @@ export class CustomKeycloak { const userId = request.kauth.grant.access_token.content.sub.split(':') request.session.userId = userId[userId.length - 1] // tslint:disable-next-line: no-console - console.log('userId ::', userId) + console.log('userId ::', userId, '------', new Date().toString()) // tslint:disable-next-line: no-console - console.log('request.session after adding userId ::', request.session, '----cookie---', request.cookies) + console.log('request.session after adding userId ::', request.session, '----cookie---', request.cookies, + '------', new Date().toString()) } catch (err) { - logError('userId conversation error' + request.kauth.grant.access_token.content.sub) + logError('userId conversation error' + request.kauth.grant.access_token.content.sub, '------', new Date().toString()) } const postLoginRequest = [] // tslint:disable-next-line: no-any postLoginRequest.push((callback: any) => { // tslint:disable-next-line: no-console - console.log('pushing task to postLoginRequest', '------', new Date()) + console.log('pushing task to postLoginRequest', '------', new Date().toString()) PERMISSION_HELPER.getCurrentUserRoles(request, callback) }) // tslint:disable-next-line: no-any - async.series(postLoginRequest, (err: any) => { + async.series(postLoginRequest, (err: any, results: any) => { if (err) { - logError('error loggin in user') + logError('error loggin in user', '------', new Date().toString()) next(err, null) } else { - logInfo(`${process.pid}: User authenticated`) + // tslint:disable-next-line: no-console + console.log('async.series results -- ', results, '------', new Date().toString()) + logInfo(`${process.pid}: User authenticated`, '------', new Date().toString()) next(null, 'loggedin') } }) diff --git a/src/utils/env.ts b/src/utils/env.ts index d04ecba8..08ae7095 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -56,6 +56,7 @@ export const CONSTANTS = { KHUB_SEARCH_BASE: env.KHUB_SEARCH_BASE || 'http://localhost:3014', KNOWLEDGE_MW_API_BASE: env.KNOWLEDGE_MW_API_BASE || 'http://knowledge-mw-service:5000', KONG_API_BASE: env.KONG_API_BASE || 'https://igot-dev.in/api', + PORTAL_CASSANDRA_CONSISTENCY_LEVEL: env.PORTAL_CASSANDRA_CONSISTENCY_LEVEL || 'one', POST_ASSESSMENT_BASE: env.POST_ASSESSMENT_BASE || 'http://localhost.com', POST_ASSESSMENT_CLIENT_ID: env.POST_ASSESSMENT_CLIENT_ID || '', POST_ASSESSMENT_CLIENT_SECRET: env.POST_ASSESSMENT_CLIENT_SECRET || diff --git a/src/utils/permissionHelper.ts b/src/utils/permissionHelper.ts index 5f597806..58715f90 100644 --- a/src/utils/permissionHelper.ts +++ b/src/utils/permissionHelper.ts @@ -25,7 +25,8 @@ export const PERMISSION_HELPER = { reqObj.session.userRoles.push('PUBLIC') } // tslint:disable-next-line: no-console - console.log('request.session after adding userId ::', reqObj.session, '----cookie---', reqObj.cookies) + console.log('request.session after adding userId ::', reqObj.session, '----cookie---', reqObj.cookies, + '------', new Date().toString()) this.createNodeBBUser(reqObj, callback) // tslint:disable-next-line: no-any // reqObj.session.save((error: any) => { @@ -41,7 +42,7 @@ export const PERMISSION_HELPER = { } else { callback('reqObj.session no session', null) } - logInfo('permission helper:: setRolesData function end') + logInfo('permission helper:: setRolesData function end', '------', new Date().toString()) }, // tslint:disable-next-line: no-any setNodeBBUID(reqObj: any, callback: any, body: any) { @@ -49,7 +50,8 @@ export const PERMISSION_HELPER = { // tslint:disable-next-line: no-any const nodeBBData: any = body // tslint:disable-next-line: no-console - console.log('request.session after adding userId ::', reqObj.session, '----cookie---', reqObj.cookies) + console.log('request.session after adding userId ::', reqObj.session, '----cookie---', reqObj.cookies, + '------', new Date().toString()) if (reqObj.session) { reqObj.session.uid = nodeBBData.data.result.userId.uid logInfo('After appending uid to session', reqObj.session.uid) @@ -57,11 +59,13 @@ export const PERMISSION_HELPER = { // tslint:disable-next-line: no-any reqObj.session.save((error: any) => { if (error) { - logError('reqObj.session.save error -- ', error) + logError('reqObj.session.save error -- ', error, '------', new Date().toString()) callback(error, null) } else { + // tslint:disable-next-line: no-console + console.log(`setNodeBBUID::Success of save -- reqObj.session ${new Date()}--- `, reqObj.session ) callback(null, nodeBBData) - } + } }) logInfo('permissionHelper:: setNodeBBUID function end', '------', new Date().toString()) }, @@ -84,14 +88,12 @@ export const PERMISSION_HELPER = { logInfo('Inside user/v2/read', '------', new Date().toString()) if (body) { // tslint:disable-next-line: no-console - console.log('------', new Date().toString()) - // tslint:disable-next-line: no-console - console.log('Success user/v2/read: body', body) + console.log('Success user/v2/read: body', body, '------', new Date().toString()) this.setRolesData(reqObj, callback, body) } if (err) { // tslint:disable-next-line: no-console - console.log('Making axios call to nodeBB ERROR -- ', err) + console.log('Making axios call to nodeBB ERROR -- ', err, '------', new Date().toString()) callback(err, null) } }) @@ -125,10 +127,10 @@ export const PERMISSION_HELPER = { console.log('NodeBB axios call response', '------', new Date().toString()) this.setNodeBBUID(reqObj, callback, nodeBBResp) } - logInfo('permissionHelper::createNodeBBUser function end') + logInfo('permissionHelper::createNodeBBUser function end', '------', new Date().toString()) } catch (err) { // tslint:disable-next-line: no-console - console.log('Making axios call to nodeBB ERROR -- ', err) + console.log('Making axios call to nodeBB ERROR -- ', err, '------', new Date().toString()) callback(err, null) } }, From 3b138ad36302f81e909e198f29bdcd411ac1cf8c Mon Sep 17 00:00:00 2001 From: "christopher.fernandes" Date: Wed, 19 Jan 2022 16:49:41 +0530 Subject: [PATCH 237/856] Task #0000: added express-cassandra pkg --- package-lock.json | 356 +++++++++++++++++++++++++--------------------- package.json | 1 + 2 files changed, 194 insertions(+), 163 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3cdc6400..6f24b6c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -383,6 +383,15 @@ "@types/vinyl": "*" } }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -519,8 +528,7 @@ "arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" }, "arr-filter": { "version": "1.1.2", @@ -534,8 +542,7 @@ "arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" }, "arr-map": { "version": "2.0.2", @@ -549,8 +556,7 @@ "arr-union": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" }, "array-each": { "version": "1.0.1", @@ -641,8 +647,7 @@ "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" }, "asn1": { "version": "0.2.4", @@ -668,11 +673,15 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" + }, "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" }, "async": { "version": "3.2.0", @@ -722,8 +731,7 @@ "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, "atomic-sleep": { "version": "1.0.0", @@ -748,6 +756,32 @@ "follow-redirects": "1.5.10" } }, + "babel-polyfill": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", + "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", + "requires": { + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "regenerator-runtime": "^0.10.5" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + } + } + }, "bach": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", @@ -775,7 +809,6 @@ "version": "0.11.2", "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, "requires": { "cache-base": "^1.0.1", "class-utils": "^0.3.5", @@ -790,7 +823,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, "requires": { "is-descriptor": "^1.0.0" } @@ -799,7 +831,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -808,7 +839,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -817,7 +847,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -935,7 +964,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, "requires": { "arr-flatten": "^1.1.0", "array-unique": "^0.3.2", @@ -953,7 +981,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, "requires": { "is-extendable": "^0.1.0" } @@ -1061,7 +1088,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, "requires": { "collection-visit": "^1.0.0", "component-emitter": "^1.2.1", @@ -1222,6 +1248,19 @@ "printj": "~1.1.2" } }, + "chai": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + } + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -1232,6 +1271,16 @@ "supports-color": "^5.3.0" } }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" + }, + "check-types": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/check-types/-/check-types-1.4.0.tgz", + "integrity": "sha1-7tY7usnqSaDiagljFAWLA7CN1is=" + }, "chokidar": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz", @@ -1262,7 +1311,6 @@ "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, "requires": { "arr-union": "^3.1.0", "define-property": "^0.2.5", @@ -1274,7 +1322,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "^0.1.0" } @@ -1410,7 +1457,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, "requires": { "map-visit": "^1.0.0", "object-visit": "^1.0.0" @@ -1457,8 +1503,7 @@ "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" }, "composable-middleware": { "version": "0.3.0", @@ -1622,8 +1667,7 @@ "copy-descriptor": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" }, "copy-props": { "version": "2.0.4", @@ -1635,6 +1679,11 @@ "is-plain-object": "^2.0.1" } }, + "core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -1750,8 +1799,7 @@ "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, "decompress": { "version": "4.2.0", @@ -1881,6 +1929,19 @@ } } }, + "deep-diff": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-0.3.8.tgz", + "integrity": "sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ=" + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "requires": { + "type-detect": "^4.0.0" + } + }, "deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -1923,7 +1984,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, "requires": { "is-descriptor": "^1.0.2", "isobject": "^3.0.1" @@ -1933,7 +1993,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -1942,7 +2001,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -1951,7 +2009,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -2332,7 +2389,6 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, "requires": { "debug": "^2.3.3", "define-property": "^0.2.5", @@ -2347,7 +2403,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -2356,7 +2411,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "^0.1.0" } @@ -2365,7 +2419,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, "requires": { "is-extendable": "^0.1.0" } @@ -2438,6 +2491,42 @@ } } }, + "express-cassandra": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/express-cassandra/-/express-cassandra-2.1.0.tgz", + "integrity": "sha1-+1vpZoEhPTxx5tQ4AMIDqbwAzM8=", + "requires": { + "JSONStream": "^1.3.1", + "async": "^1.5.2", + "babel-polyfill": "^6.26.0", + "bluebird": "^3.4.6", + "cassandra-driver": "^3.3.0", + "chai": "^4.1.2", + "check-types": "^1.4.0", + "debug": "^3.1.0", + "deep-diff": "^0.3.4", + "lodash": "^4.14.2", + "object-hash": "1.1.4", + "readdirp": "^2.1.0", + "readline-sync": "^1.4.4", + "semver": "^5.4.1" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "cassandra-driver": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/cassandra-driver/-/cassandra-driver-3.6.0.tgz", + "integrity": "sha512-CkN3V+oPaF5RvakUjD3uUjEm8f6U8S0aT1+YqeQsVT3UDpPT2K8SOdNDEHA1KjamakHch6zkDgHph1xWyqBGGw==", + "requires": { + "long": "^2.2.0" + } + } + } + }, "express-fileupload": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/express-fileupload/-/express-fileupload-1.1.4.tgz", @@ -2514,7 +2603,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, "requires": { "assign-symbols": "^1.0.0", "is-extendable": "^1.0.1" @@ -2524,7 +2612,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, "requires": { "is-plain-object": "^2.0.4" } @@ -2535,7 +2622,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, "requires": { "array-unique": "^0.3.2", "define-property": "^1.0.0", @@ -2551,7 +2637,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, "requires": { "is-descriptor": "^1.0.0" } @@ -2560,7 +2645,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, "requires": { "is-extendable": "^0.1.0" } @@ -2569,7 +2653,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -2578,7 +2661,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -2587,7 +2669,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -2674,7 +2755,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, "requires": { "extend-shallow": "^2.0.1", "is-number": "^3.0.0", @@ -2686,7 +2766,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, "requires": { "is-extendable": "^0.1.0" } @@ -2783,8 +2862,7 @@ "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" }, "for-own": { "version": "1.0.0", @@ -2824,7 +2902,6 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, "requires": { "map-cache": "^0.2.2" } @@ -3431,6 +3508,11 @@ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" + }, "get-proxy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz", @@ -3455,8 +3537,7 @@ "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" }, "getpass": { "version": "0.1.7", @@ -3618,8 +3699,7 @@ "graceful-fs": { "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true + "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" }, "graceful-readlink": { "version": "1.0.1", @@ -3781,7 +3861,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, "requires": { "get-value": "^2.0.6", "has-values": "^1.0.0", @@ -3792,7 +3871,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, "requires": { "is-number": "^3.0.0", "kind-of": "^4.0.0" @@ -3801,14 +3879,12 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "kind-of": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -4150,7 +4226,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, "requires": { "kind-of": "^3.0.2" }, @@ -4158,14 +4233,12 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -4205,7 +4278,6 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, "requires": { "kind-of": "^3.0.2" }, @@ -4213,14 +4285,12 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -4231,7 +4301,6 @@ "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, "requires": { "is-accessor-descriptor": "^0.1.6", "is-data-descriptor": "^0.1.4", @@ -4241,8 +4310,7 @@ "kind-of": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" } } }, @@ -4255,8 +4323,7 @@ "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" }, "is-extglob": { "version": "2.1.1", @@ -4311,7 +4378,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, "requires": { "kind-of": "^3.0.2" }, @@ -4319,14 +4385,12 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -4390,7 +4454,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, "requires": { "isobject": "^3.0.1" } @@ -4451,14 +4514,12 @@ "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, "isexe": { "version": "2.0.0", @@ -4469,8 +4530,7 @@ "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" }, "isstream": { "version": "0.1.2", @@ -4541,6 +4601,11 @@ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -4613,8 +4678,7 @@ "kind-of": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" }, "last-run": { "version": "1.1.1", @@ -4783,14 +4847,12 @@ "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" }, "map-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, "requires": { "object-visit": "^1.0.0" } @@ -4855,7 +4917,6 @@ "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, "requires": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", @@ -4925,7 +4986,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, "requires": { "for-in": "^1.0.2", "is-extendable": "^1.0.1" @@ -4935,7 +4995,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, "requires": { "is-plain-object": "^2.0.4" } @@ -5003,7 +5062,6 @@ "version": "1.2.13", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, "requires": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", @@ -5170,7 +5228,6 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, "requires": { "copy-descriptor": "^0.1.0", "define-property": "^0.2.5", @@ -5181,7 +5238,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "^0.1.0" } @@ -5189,20 +5245,23 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "^1.1.5" } } } }, + "object-hash": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.1.4.tgz", + "integrity": "sha1-E28H8aSK9fa5BoEkUa5OQ5eMlKo=" + }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -5213,7 +5272,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, "requires": { "isobject": "^3.0.0" } @@ -5256,7 +5314,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, "requires": { "isobject": "^3.0.1" } @@ -5441,8 +5498,7 @@ "pascalcase": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" }, "path-dirname": { "version": "1.0.2", @@ -5519,6 +5575,11 @@ } } }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==" + }, "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -5631,8 +5692,7 @@ "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" }, "prepend-http": { "version": "1.0.4", @@ -5654,8 +5714,7 @@ "process-nextick-args": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" }, "progress": { "version": "2.0.3", @@ -5845,7 +5904,6 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -5860,13 +5918,17 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, "requires": { "graceful-fs": "^4.1.11", "micromatch": "^3.1.10", "readable-stream": "^2.0.2" } }, + "readline-sync": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/readline-sync/-/readline-sync-1.4.10.tgz", + "integrity": "sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==" + }, "rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", @@ -5887,11 +5949,15 @@ "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", "dev": true }, + "regenerator-runtime": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", + "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=" + }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, "requires": { "extend-shallow": "^3.0.2", "safe-regex": "^1.1.0" @@ -5954,14 +6020,12 @@ "repeat-element": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" }, "repeat-string": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" }, "replace-ext": { "version": "1.0.0", @@ -6078,8 +6142,7 @@ "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" }, "responselike": { "version": "1.0.2", @@ -6093,8 +6156,7 @@ "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" }, "rimraf": { "version": "2.6.3", @@ -6120,7 +6182,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, "requires": { "ret": "~0.1.10" } @@ -6153,8 +6214,7 @@ "semver": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" }, "semver-compare": { "version": "1.0.0", @@ -6243,7 +6303,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, "requires": { "extend-shallow": "^2.0.1", "is-extendable": "^0.1.1", @@ -6255,7 +6314,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, "requires": { "is-extendable": "^0.1.0" } @@ -6304,7 +6362,6 @@ "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, "requires": { "base": "^0.11.1", "debug": "^2.2.0", @@ -6320,7 +6377,6 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, "requires": { "ms": "2.0.0" } @@ -6329,7 +6385,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "^0.1.0" } @@ -6338,7 +6393,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, "requires": { "is-extendable": "^0.1.0" } @@ -6349,7 +6403,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, "requires": { "define-property": "^1.0.0", "isobject": "^3.0.0", @@ -6360,7 +6413,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, "requires": { "is-descriptor": "^1.0.0" } @@ -6369,7 +6421,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -6378,7 +6429,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, "requires": { "kind-of": "^6.0.0" } @@ -6387,7 +6437,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, "requires": { "is-accessor-descriptor": "^1.0.0", "is-data-descriptor": "^1.0.0", @@ -6400,7 +6449,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, "requires": { "kind-of": "^3.2.0" }, @@ -6408,14 +6456,12 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -6524,14 +6570,12 @@ "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, "source-map-resolve": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", - "dev": true, "requires": { "atob": "^2.1.1", "decode-uri-component": "^0.2.0", @@ -6561,8 +6605,7 @@ "source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" }, "sparkles": { "version": "1.0.1", @@ -6606,7 +6649,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, "requires": { "extend-shallow": "^3.0.0" } @@ -6651,7 +6693,6 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, "requires": { "define-property": "^0.2.5", "object-copy": "^0.1.0" @@ -6661,7 +6702,6 @@ "version": "0.2.5", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, "requires": { "is-descriptor": "^0.1.0" } @@ -6710,7 +6750,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, "requires": { "safe-buffer": "~5.1.0" } @@ -6808,8 +6847,7 @@ "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "through2": { "version": "2.0.5", @@ -6863,7 +6901,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, "requires": { "kind-of": "^3.0.2" }, @@ -6871,14 +6908,12 @@ "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, "kind-of": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, "requires": { "is-buffer": "^1.1.5" } @@ -6889,7 +6924,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, "requires": { "define-property": "^2.0.2", "extend-shallow": "^3.0.2", @@ -6901,7 +6935,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, "requires": { "is-number": "^3.0.0", "repeat-string": "^1.6.1" @@ -7104,6 +7137,11 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" + }, "type-fest": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz", @@ -7202,7 +7240,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, "requires": { "arr-union": "^3.1.0", "get-value": "^2.0.6", @@ -7238,7 +7275,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, "requires": { "has-value": "^0.3.1", "isobject": "^3.0.0" @@ -7248,7 +7284,6 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, "requires": { "get-value": "^2.0.3", "has-values": "^0.1.4", @@ -7259,7 +7294,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, "requires": { "isarray": "1.0.0" } @@ -7269,8 +7303,7 @@ "has-values": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" } } }, @@ -7315,8 +7348,7 @@ "urix": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" }, "url-join": { "version": "4.0.1", @@ -7346,14 +7378,12 @@ "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "utils-merge": { "version": "1.0.1", diff --git a/package.json b/package.json index 927bbda5..dedfb377 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "date-fns": "^2.0.1", "dateformat": "^4.5.1", "express": "^4.17.1", + "express-cassandra": "2.1.0", "express-fileupload": "^1.1.4", "express-healthcheck": "^0.1.0", "express-session": "^1.16.1", From b29a121aab8bf4a6641a8bef65d541963de0ce11 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Wed, 19 Jan 2022 18:08:21 +0530 Subject: [PATCH 238/856] added logs --- src/protectedApi_v8/contentprivate.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/protectedApi_v8/contentprivate.ts b/src/protectedApi_v8/contentprivate.ts index ff1fb28a..af31e0dd 100644 --- a/src/protectedApi_v8/contentprivate.ts +++ b/src/protectedApi_v8/contentprivate.ts @@ -3,7 +3,7 @@ import { Router } from 'express' import { axiosRequestConfig } from '../configs/request.config' import { CONSTANTS } from '../utils/env' -import { logError } from '../utils/logger' +import { logError, logInfo } from '../utils/logger' import { ERROR } from '../utils/message' import { extractUserId, extractUserToken } from '../utils/requestExtract' @@ -33,6 +33,7 @@ contentPrivateApi.patch('/update/:id', async (req, res) => { res.status(400).send(userIdFailedMessage) return } + logInfo('line no: 36 ===> ', id, JSON.stringify(fields), userId, userToken) if (fields instanceof Array) { for (const entry of fields) { if (editableFields.indexOf(entry) === -1) { @@ -46,6 +47,7 @@ contentPrivateApi.patch('/update/:id', async (req, res) => { } const userChannel = getUserChannel(userToken, userId) const hierarchySource = getHierarchyDetails(userToken, id) + logInfo('line no: 50 ===> ') if (userChannel !== hierarchySource) { res.status(400).send({ msg: res.status(400).send({ @@ -57,7 +59,7 @@ contentPrivateApi.patch('/update/:id', async (req, res) => { API_END_POINTS.updateContentEndPoint(id), req.body, { - ...axiosRequestConfig, + ...]axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, // tslint:disable-next-line: all @@ -65,6 +67,7 @@ contentPrivateApi.patch('/update/:id', async (req, res) => { }, } ) + logInfo('line no: 70 ===> ', JSON.stringify(response.status), response.data) res.status(response.status).send(response.data) } catch (err) { logError(Error + err) @@ -89,7 +92,7 @@ contentPrivateApi.patch('/migratereviewer/:id', async (req, res) => { } if (fields instanceof Array) { for (const entry of fields) { - if (editableFieldsReviewer.indexOf(entry) === -1 && fields.length === editableFieldsReviewer.length ) { + if (editableFieldsReviewer.indexOf(entry) === -1 && fields.length === editableFieldsReviewer.length) { res.status(400).send({ msg: res.status(400).send({ msg: FIELD_VALIDATION_ERROR, @@ -143,7 +146,7 @@ contentPrivateApi.patch('/migratepublisher/:id', async (req, res) => { } if (fields instanceof Array) { for (const entry of fields) { - if (editableFieldsPublisher.indexOf(entry) === -1 && fields.length === editableFieldsPublisher.length ) { + if (editableFieldsPublisher.indexOf(entry) === -1 && fields.length === editableFieldsPublisher.length) { res.status(400).send({ msg: res.status(400).send({ msg: FIELD_VALIDATION_ERROR, @@ -223,3 +226,7 @@ export async function getUserChannel(token: string, userId: string) { return 'userChannelDetails' } } +<<<<<<< Updated upstream +======= + +>>>>>>> Stashed changes From 88e594b5e0e56c53455974eb89a0605a6b97e497 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Wed, 19 Jan 2022 18:12:40 +0530 Subject: [PATCH 239/856] resolved conflict error --- .huskyrc.json | 2 -- src/protectedApi_v8/contentprivate.ts | 10 +++------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/.huskyrc.json b/.huskyrc.json index 2487ddb7..78760b61 100644 --- a/.huskyrc.json +++ b/.huskyrc.json @@ -1,6 +1,4 @@ { "hooks": { - "pre-commit": "", - "pre-push": "npm run build" } } \ No newline at end of file diff --git a/src/protectedApi_v8/contentprivate.ts b/src/protectedApi_v8/contentprivate.ts index af31e0dd..42f2a1f1 100644 --- a/src/protectedApi_v8/contentprivate.ts +++ b/src/protectedApi_v8/contentprivate.ts @@ -59,7 +59,7 @@ contentPrivateApi.patch('/update/:id', async (req, res) => { API_END_POINTS.updateContentEndPoint(id), req.body, { - ...]axiosRequestConfig, + ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, // tslint:disable-next-line: all @@ -92,7 +92,7 @@ contentPrivateApi.patch('/migratereviewer/:id', async (req, res) => { } if (fields instanceof Array) { for (const entry of fields) { - if (editableFieldsReviewer.indexOf(entry) === -1 && fields.length === editableFieldsReviewer.length) { + if (editableFieldsReviewer.indexOf(entry) === -1 && fields.length === editableFieldsReviewer.length ) { res.status(400).send({ msg: res.status(400).send({ msg: FIELD_VALIDATION_ERROR, @@ -146,7 +146,7 @@ contentPrivateApi.patch('/migratepublisher/:id', async (req, res) => { } if (fields instanceof Array) { for (const entry of fields) { - if (editableFieldsPublisher.indexOf(entry) === -1 && fields.length === editableFieldsPublisher.length) { + if (editableFieldsPublisher.indexOf(entry) === -1 && fields.length === editableFieldsPublisher.length ) { res.status(400).send({ msg: res.status(400).send({ msg: FIELD_VALIDATION_ERROR, @@ -226,7 +226,3 @@ export async function getUserChannel(token: string, userId: string) { return 'userChannelDetails' } } -<<<<<<< Updated upstream -======= - ->>>>>>> Stashed changes From 4c721c494246885f9e15fbf9cdfb4ff2bb8625f7 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Wed, 19 Jan 2022 18:14:35 +0530 Subject: [PATCH 240/856] added hook --- .huskyrc.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.huskyrc.json b/.huskyrc.json index 78760b61..2487ddb7 100644 --- a/.huskyrc.json +++ b/.huskyrc.json @@ -1,4 +1,6 @@ { "hooks": { + "pre-commit": "", + "pre-push": "npm run build" } } \ No newline at end of file From e8d8a4c885ae9fb06147e055b4607fc4b229b5be Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Thu, 20 Jan 2022 01:47:51 +0530 Subject: [PATCH 241/856] initial commit --- src/protectedApi_v8/contentprivate.ts | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/protectedApi_v8/contentprivate.ts b/src/protectedApi_v8/contentprivate.ts index 42f2a1f1..281f0eab 100644 --- a/src/protectedApi_v8/contentprivate.ts +++ b/src/protectedApi_v8/contentprivate.ts @@ -14,8 +14,8 @@ const API_END_POINTS = { readUserEndPoint: (userId: string) => `${CONSTANTS.KONG_API_BASE}/user/v2/read/${userId}`, updateContentEndPoint: (id: string) => `${CONSTANTS.KONG_API_BASE}/private/content/v3/update/${id}`, } - -const editableFields = ['versionKey', 'createdBy', 'creatorContacts'] +// tslint:disable-next-line: no-commented-code +// const editableFields = ['versionKey', 'createdBy', 'creatorContacts'] const editableFieldsReviewer = ['versionKey', 'isExternal', 'reviewer', 'reviewerIDs'] const editableFieldsPublisher = ['versionKey', 'isExternal', 'publisherIDs: ', 'publisherDetails'] const userIdFailedMessage = 'NO_USER_ID' @@ -34,17 +34,18 @@ contentPrivateApi.patch('/update/:id', async (req, res) => { return } logInfo('line no: 36 ===> ', id, JSON.stringify(fields), userId, userToken) - if (fields instanceof Array) { - for (const entry of fields) { - if (editableFields.indexOf(entry) === -1) { - res.status(400).send({ - msg: res.status(400).send({ - msg: FIELD_VALIDATION_ERROR, - }), - }) - } - } - } + // tslint:disable-next-line: no-commented-code + // if (fields instanceof Array) { + // for (const entry of fields) { + // if (editableFields.indexOf(entry) === -1) { + // res.status(400).send({ + // msg: res.status(400).send({ + // msg: FIELD_VALIDATION_ERROR, + // }), + // }) + // } + // } + // } const userChannel = getUserChannel(userToken, userId) const hierarchySource = getHierarchyDetails(userToken, id) logInfo('line no: 50 ===> ') From 1339d2d4b828d73bc44aeafd1b697950e18e9239 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Thu, 20 Jan 2022 02:06:41 +0530 Subject: [PATCH 242/856] added more logs --- src/protectedApi_v8/contentprivate.ts | 28 +++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/protectedApi_v8/contentprivate.ts b/src/protectedApi_v8/contentprivate.ts index 281f0eab..e1265b9f 100644 --- a/src/protectedApi_v8/contentprivate.ts +++ b/src/protectedApi_v8/contentprivate.ts @@ -15,7 +15,7 @@ const API_END_POINTS = { updateContentEndPoint: (id: string) => `${CONSTANTS.KONG_API_BASE}/private/content/v3/update/${id}`, } // tslint:disable-next-line: no-commented-code -// const editableFields = ['versionKey', 'createdBy', 'creatorContacts'] +const editableFields = ['versionKey', 'createdBy', 'creatorContacts'] const editableFieldsReviewer = ['versionKey', 'isExternal', 'reviewer', 'reviewerIDs'] const editableFieldsPublisher = ['versionKey', 'isExternal', 'publisherIDs: ', 'publisherDetails'] const userIdFailedMessage = 'NO_USER_ID' @@ -35,17 +35,17 @@ contentPrivateApi.patch('/update/:id', async (req, res) => { } logInfo('line no: 36 ===> ', id, JSON.stringify(fields), userId, userToken) // tslint:disable-next-line: no-commented-code - // if (fields instanceof Array) { - // for (const entry of fields) { - // if (editableFields.indexOf(entry) === -1) { - // res.status(400).send({ - // msg: res.status(400).send({ - // msg: FIELD_VALIDATION_ERROR, - // }), - // }) - // } - // } - // } + if (fields instanceof Array) { + for (const entry of fields) { + if (editableFields.indexOf(entry) === -1) { + res.status(400).send({ + msg: res.status(400).send({ + msg: FIELD_VALIDATION_ERROR, + }), + }) + } + } + } const userChannel = getUserChannel(userToken, userId) const hierarchySource = getHierarchyDetails(userToken, id) logInfo('line no: 50 ===> ') @@ -198,7 +198,9 @@ export async function getHierarchyDetails(token: string, id: string) { 'x-authenticated-user-token': token, }, }) + logInfo('line 201 ====>',response.data) const hierarchyResult = response.data.result.content + logInfo('line 202 ====>',hierarchyResult) if (typeof hierarchyResult !== 'undefined' && hierarchyResult != null) { return hierarchyResult.source } @@ -218,7 +220,9 @@ export async function getUserChannel(token: string, userId: string) { 'x-authenticated-user-token': token, }, }) + logInfo('line 222 ====>',response.data) const userProfileResult = response.data.result.response + logInfo('line 222 ====>',userProfileResult) if (typeof userProfileResult !== 'undefined' && userProfileResult != null) { return userProfileResult.channel } From eacae25d7b6fdb6605398ebd9d6469e2d3ce9eef Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Thu, 20 Jan 2022 02:07:53 +0530 Subject: [PATCH 243/856] added more logs --- src/protectedApi_v8/contentprivate.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/protectedApi_v8/contentprivate.ts b/src/protectedApi_v8/contentprivate.ts index e1265b9f..ab15b632 100644 --- a/src/protectedApi_v8/contentprivate.ts +++ b/src/protectedApi_v8/contentprivate.ts @@ -198,9 +198,9 @@ export async function getHierarchyDetails(token: string, id: string) { 'x-authenticated-user-token': token, }, }) - logInfo('line 201 ====>',response.data) + logInfo('line 201 ====>', response.data) const hierarchyResult = response.data.result.content - logInfo('line 202 ====>',hierarchyResult) + logInfo('line 202 ====>', hierarchyResult) if (typeof hierarchyResult !== 'undefined' && hierarchyResult != null) { return hierarchyResult.source } @@ -220,9 +220,9 @@ export async function getUserChannel(token: string, userId: string) { 'x-authenticated-user-token': token, }, }) - logInfo('line 222 ====>',response.data) + logInfo('line 222 ====>', response.data) const userProfileResult = response.data.result.response - logInfo('line 222 ====>',userProfileResult) + logInfo('line 222 ====>', userProfileResult) if (typeof userProfileResult !== 'undefined' && userProfileResult != null) { return userProfileResult.channel } From 5d6412bd8b87bfd758cf9228f7e86a0725dd3884 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Thu, 20 Jan 2022 02:22:20 +0530 Subject: [PATCH 244/856] removed validations --- src/protectedApi_v8/contentprivate.ts | 58 ++++++++++++++------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/src/protectedApi_v8/contentprivate.ts b/src/protectedApi_v8/contentprivate.ts index ab15b632..852b0f3c 100644 --- a/src/protectedApi_v8/contentprivate.ts +++ b/src/protectedApi_v8/contentprivate.ts @@ -25,37 +25,40 @@ const CHANNEL_VALIDATION_ERROR = 'SOURCE_MISMATCH_ERROR' contentPrivateApi.patch('/update/:id', async (req, res) => { try { const id = req.params.id - const content = req.body.content - const fields = Object.keys(content) + // tslint:disable-next-line: no-commented-code + // const content = req.body.content + // const fields = Object.keys(content) const userId = extractUserId(req) const userToken = extractUserToken(req) as string if (!userId) { res.status(400).send(userIdFailedMessage) return } - logInfo('line no: 36 ===> ', id, JSON.stringify(fields), userId, userToken) // tslint:disable-next-line: no-commented-code - if (fields instanceof Array) { - for (const entry of fields) { - if (editableFields.indexOf(entry) === -1) { - res.status(400).send({ - msg: res.status(400).send({ - msg: FIELD_VALIDATION_ERROR, - }), - }) - } - } - } - const userChannel = getUserChannel(userToken, userId) - const hierarchySource = getHierarchyDetails(userToken, id) - logInfo('line no: 50 ===> ') - if (userChannel !== hierarchySource) { - res.status(400).send({ - msg: res.status(400).send({ - msg: CHANNEL_VALIDATION_ERROR, - }), - }) - } + // logInfo('line no: 36 ===> ', id, JSON.stringify(fields), userId, userToken) + // tslint:disable-next-line: no-commented-code + // if (fields instanceof Array) { + // for (const entry of fields) { + // if (editableFields.indexOf(entry) === -1) { + // res.status(400).send({ + // msg: res.status(400).send({ + // msg: FIELD_VALIDATION_ERROR, + // }), + // }) + // } + // } + // } + // tslint:disable-next-line: no-commented-code + // const userChannel = getUserChannel(userToken, userId) + // const hierarchySource = getHierarchyDetails(userToken, id) + // logInfo('line no: 50 ===> ') + // if (userChannel !== hierarchySource) { + // res.status(400).send({ + // msg: res.status(400).send({ + // msg: CHANNEL_VALIDATION_ERROR, + // }), + // }) + // } const response = await axios.patch( API_END_POINTS.updateContentEndPoint(id), req.body, @@ -68,7 +71,8 @@ contentPrivateApi.patch('/update/:id', async (req, res) => { }, } ) - logInfo('line no: 70 ===> ', JSON.stringify(response.status), response.data) + // tslint:disable-next-line: no-commented-code + // logInfo('line no: 70 ===> ', JSON.stringify(response.status), response.data) res.status(response.status).send(response.data) } catch (err) { logError(Error + err) @@ -93,7 +97,7 @@ contentPrivateApi.patch('/migratereviewer/:id', async (req, res) => { } if (fields instanceof Array) { for (const entry of fields) { - if (editableFieldsReviewer.indexOf(entry) === -1 && fields.length === editableFieldsReviewer.length ) { + if (editableFieldsReviewer.indexOf(entry) === -1 && fields.length === editableFieldsReviewer.length) { res.status(400).send({ msg: res.status(400).send({ msg: FIELD_VALIDATION_ERROR, @@ -147,7 +151,7 @@ contentPrivateApi.patch('/migratepublisher/:id', async (req, res) => { } if (fields instanceof Array) { for (const entry of fields) { - if (editableFieldsPublisher.indexOf(entry) === -1 && fields.length === editableFieldsPublisher.length ) { + if (editableFieldsPublisher.indexOf(entry) === -1 && fields.length === editableFieldsPublisher.length) { res.status(400).send({ msg: res.status(400).send({ msg: FIELD_VALIDATION_ERROR, From 3ce65850d76b6306697e2a3380922b495a7874a4 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Thu, 20 Jan 2022 02:30:44 +0530 Subject: [PATCH 245/856] fix lint error --- src/protectedApi_v8/contentprivate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protectedApi_v8/contentprivate.ts b/src/protectedApi_v8/contentprivate.ts index 852b0f3c..2f641bab 100644 --- a/src/protectedApi_v8/contentprivate.ts +++ b/src/protectedApi_v8/contentprivate.ts @@ -15,7 +15,7 @@ const API_END_POINTS = { updateContentEndPoint: (id: string) => `${CONSTANTS.KONG_API_BASE}/private/content/v3/update/${id}`, } // tslint:disable-next-line: no-commented-code -const editableFields = ['versionKey', 'createdBy', 'creatorContacts'] +// const editableFields = ['versionKey', 'createdBy', 'creatorContacts'] const editableFieldsReviewer = ['versionKey', 'isExternal', 'reviewer', 'reviewerIDs'] const editableFieldsPublisher = ['versionKey', 'isExternal', 'publisherIDs: ', 'publisherDetails'] const userIdFailedMessage = 'NO_USER_ID' From ba9f3bec4f06de7a45be56d1f93507376de6991f Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Thu, 20 Jan 2022 02:43:22 +0530 Subject: [PATCH 246/856] added await --- src/protectedApi_v8/contentprivate.ts | 62 +++++++++++++-------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/protectedApi_v8/contentprivate.ts b/src/protectedApi_v8/contentprivate.ts index 2f641bab..430414b5 100644 --- a/src/protectedApi_v8/contentprivate.ts +++ b/src/protectedApi_v8/contentprivate.ts @@ -15,7 +15,7 @@ const API_END_POINTS = { updateContentEndPoint: (id: string) => `${CONSTANTS.KONG_API_BASE}/private/content/v3/update/${id}`, } // tslint:disable-next-line: no-commented-code -// const editableFields = ['versionKey', 'createdBy', 'creatorContacts'] +const editableFields = ['versionKey', 'createdBy', 'creatorContacts'] const editableFieldsReviewer = ['versionKey', 'isExternal', 'reviewer', 'reviewerIDs'] const editableFieldsPublisher = ['versionKey', 'isExternal', 'publisherIDs: ', 'publisherDetails'] const userIdFailedMessage = 'NO_USER_ID' @@ -26,39 +26,39 @@ contentPrivateApi.patch('/update/:id', async (req, res) => { try { const id = req.params.id // tslint:disable-next-line: no-commented-code - // const content = req.body.content - // const fields = Object.keys(content) + const content = req.body.content + const fields = Object.keys(content) const userId = extractUserId(req) const userToken = extractUserToken(req) as string if (!userId) { res.status(400).send(userIdFailedMessage) return } + // tslint: disable - next - line: no - commented - code + logInfo('line no: 36 ===> ', id, JSON.stringify(fields), userId, userToken) + // tslint: disable - next - line: no - commented - code + if (fields instanceof Array) { + for (const entry of fields) { + if (editableFields.indexOf(entry) === -1) { + res.status(400).send({ + msg: res.status(400).send({ + msg: FIELD_VALIDATION_ERROR, + }), + }) + } + } + } // tslint:disable-next-line: no-commented-code - // logInfo('line no: 36 ===> ', id, JSON.stringify(fields), userId, userToken) - // tslint:disable-next-line: no-commented-code - // if (fields instanceof Array) { - // for (const entry of fields) { - // if (editableFields.indexOf(entry) === -1) { - // res.status(400).send({ - // msg: res.status(400).send({ - // msg: FIELD_VALIDATION_ERROR, - // }), - // }) - // } - // } - // } - // tslint:disable-next-line: no-commented-code - // const userChannel = getUserChannel(userToken, userId) - // const hierarchySource = getHierarchyDetails(userToken, id) - // logInfo('line no: 50 ===> ') - // if (userChannel !== hierarchySource) { - // res.status(400).send({ - // msg: res.status(400).send({ - // msg: CHANNEL_VALIDATION_ERROR, - // }), - // }) - // } + const userChannel = await getUserChannel(userToken, userId) + const hierarchySource = await getHierarchyDetails(userToken, id) + logInfo('line no: 50 ===> ', userChannel, hierarchySource) + if (userChannel !== hierarchySource) { + res.status(400).send({ + msg: res.status(400).send({ + msg: CHANNEL_VALIDATION_ERROR, + }), + }) + } const response = await axios.patch( API_END_POINTS.updateContentEndPoint(id), req.body, @@ -106,8 +106,8 @@ contentPrivateApi.patch('/migratereviewer/:id', async (req, res) => { } } } - const userChannel = getUserChannel(userToken, userId) - const hierarchySource = getHierarchyDetails(userToken, id) + const userChannel = await getUserChannel(userToken, userId) + const hierarchySource = await getHierarchyDetails(userToken, id) if (userChannel !== hierarchySource) { res.status(400).send({ msg: res.status(400).send({ @@ -160,8 +160,8 @@ contentPrivateApi.patch('/migratepublisher/:id', async (req, res) => { } } } - const userChannel = getUserChannel(userToken, userId) - const hierarchySource = getHierarchyDetails(userToken, id) + const userChannel = await getUserChannel(userToken, userId) + const hierarchySource = await getHierarchyDetails(userToken, id) if (userChannel !== hierarchySource) { res.status(400).send({ msg: res.status(400).send({ From 94c92bae3a8cbe9cd15d7b454689ad05081b59ef Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Thu, 20 Jan 2022 02:55:35 +0530 Subject: [PATCH 247/856] commenting out --- src/protectedApi_v8/contentprivate.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/protectedApi_v8/contentprivate.ts b/src/protectedApi_v8/contentprivate.ts index 430414b5..c5c61206 100644 --- a/src/protectedApi_v8/contentprivate.ts +++ b/src/protectedApi_v8/contentprivate.ts @@ -49,16 +49,16 @@ contentPrivateApi.patch('/update/:id', async (req, res) => { } } // tslint:disable-next-line: no-commented-code - const userChannel = await getUserChannel(userToken, userId) - const hierarchySource = await getHierarchyDetails(userToken, id) - logInfo('line no: 50 ===> ', userChannel, hierarchySource) - if (userChannel !== hierarchySource) { - res.status(400).send({ - msg: res.status(400).send({ - msg: CHANNEL_VALIDATION_ERROR, - }), - }) - } + // const userChannel = await getUserChannel(userToken, userId) + // const hierarchySource = await getHierarchyDetails(userToken, id) + // logInfo('line no: 50 ===> ', userChannel, hierarchySource) + // if (userChannel !== hierarchySource) { + // res.status(400).send({ + // msg: res.status(400).send({ + // msg: CHANNEL_VALIDATION_ERROR, + // }), + // }) + // } const response = await axios.patch( API_END_POINTS.updateContentEndPoint(id), req.body, From af2dbd0c595b9fcfe90882cc799030bc0ecabf25 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Thu, 20 Jan 2022 03:06:31 +0530 Subject: [PATCH 248/856] fixed some error --- src/protectedApi_v8/contentprivate.ts | 42 +++++++++++++-------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/src/protectedApi_v8/contentprivate.ts b/src/protectedApi_v8/contentprivate.ts index c5c61206..67933cd9 100644 --- a/src/protectedApi_v8/contentprivate.ts +++ b/src/protectedApi_v8/contentprivate.ts @@ -15,7 +15,7 @@ const API_END_POINTS = { updateContentEndPoint: (id: string) => `${CONSTANTS.KONG_API_BASE}/private/content/v3/update/${id}`, } // tslint:disable-next-line: no-commented-code -const editableFields = ['versionKey', 'createdBy', 'creatorContacts'] +// const editableFields = ['versionKey', 'createdBy', 'creatorContacts'] const editableFieldsReviewer = ['versionKey', 'isExternal', 'reviewer', 'reviewerIDs'] const editableFieldsPublisher = ['versionKey', 'isExternal', 'publisherIDs: ', 'publisherDetails'] const userIdFailedMessage = 'NO_USER_ID' @@ -37,28 +37,26 @@ contentPrivateApi.patch('/update/:id', async (req, res) => { // tslint: disable - next - line: no - commented - code logInfo('line no: 36 ===> ', id, JSON.stringify(fields), userId, userToken) // tslint: disable - next - line: no - commented - code - if (fields instanceof Array) { - for (const entry of fields) { - if (editableFields.indexOf(entry) === -1) { - res.status(400).send({ - msg: res.status(400).send({ - msg: FIELD_VALIDATION_ERROR, - }), - }) - } - } - } - // tslint:disable-next-line: no-commented-code - // const userChannel = await getUserChannel(userToken, userId) - // const hierarchySource = await getHierarchyDetails(userToken, id) - // logInfo('line no: 50 ===> ', userChannel, hierarchySource) - // if (userChannel !== hierarchySource) { - // res.status(400).send({ - // msg: res.status(400).send({ - // msg: CHANNEL_VALIDATION_ERROR, - // }), - // }) + // if (fields instanceof Array) { + // for (const entry of fields) { + // if (editableFields.indexOf(entry) === -1) { + // res.status(400).send({ + // msg: FIELD_VALIDATION_ERROR + // }) + // } + // } // } + // tslint:disable-next-line: no-commented-code + const userChannel = await getUserChannel(userToken, userId) + const hierarchySource = await getHierarchyDetails(userToken, id) + logInfo('line no: 50 ===> ', userChannel, hierarchySource) + if (userChannel !== hierarchySource) { + res.status(400).send({ + msg: res.status(400).send({ + msg: CHANNEL_VALIDATION_ERROR, + }), + }) + } const response = await axios.patch( API_END_POINTS.updateContentEndPoint(id), req.body, From ea489fecd01a8dfc4f17271945b617f4219469f9 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Thu, 20 Jan 2022 03:08:18 +0530 Subject: [PATCH 249/856] more changes --- src/protectedApi_v8/contentprivate.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/protectedApi_v8/contentprivate.ts b/src/protectedApi_v8/contentprivate.ts index 67933cd9..40057c9d 100644 --- a/src/protectedApi_v8/contentprivate.ts +++ b/src/protectedApi_v8/contentprivate.ts @@ -52,9 +52,7 @@ contentPrivateApi.patch('/update/:id', async (req, res) => { logInfo('line no: 50 ===> ', userChannel, hierarchySource) if (userChannel !== hierarchySource) { res.status(400).send({ - msg: res.status(400).send({ - msg: CHANNEL_VALIDATION_ERROR, - }), + msg: CHANNEL_VALIDATION_ERROR, }) } const response = await axios.patch( From d676a173dbe53a1648ede729e13c2e56a7bc69f8 Mon Sep 17 00:00:00 2001 From: Nivetha-M <34033831+Nivetha-M@users.noreply.github.com> Date: Fri, 21 Jan 2022 13:49:07 +0530 Subject: [PATCH 250/856] User enrolment list admin API --- src/utils/whitelistApis.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 6a54f20d..d68bcd48 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1562,6 +1562,15 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/learner/course/v2/user/enrollment/admin/list': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ROLE.MDO_ADMIN, + ROLE.CBP_ADMIN, + ], + }, }, URL_PATTERN: [ @@ -1770,5 +1779,6 @@ export const API_LIST = { '/proxies/v8/storage/upload', '/proxies/v8/storage/delete', '/proxies/v8/user/v1/extPatch', + '/proxies/v8/learner/course/v2/user/enrollment/admin/list', ], } From 7d505916ed2f66cf7def80d916801eccd2d59312 Mon Sep 17 00:00:00 2001 From: Nivetha-M <34033831+Nivetha-M@users.noreply.github.com> Date: Fri, 21 Jan 2022 13:49:48 +0530 Subject: [PATCH 251/856] Added CBP admin role --- src/utils/roles.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/roles.ts b/src/utils/roles.ts index 97fb8f97..97c93e50 100644 --- a/src/utils/roles.ts +++ b/src/utils/roles.ts @@ -2,6 +2,7 @@ export const ROLE = { CBC_ADMIN: 'CBC_ADMIN', CBC_MEMBER: 'CBC_MEMBER', + CBP_ADMIN: 'CBP_ADMIN', CONTENT_CREATOR: 'CONTENT_CREATOR', CONTENT_PUBLISHER: 'CONTENT_PUBLISHER', CONTENT_REVIEWER: 'CONTENT_REVIEWER', From c984a0e5a06509ebed4bd45a05a12991cb50d771 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Mon, 24 Jan 2022 11:33:02 +0530 Subject: [PATCH 252/856] changes in reviwer and publisher api --- src/protectedApi_v8/contentprivate.ts | 39 +++++++++++++-------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/protectedApi_v8/contentprivate.ts b/src/protectedApi_v8/contentprivate.ts index 40057c9d..1dd143fa 100644 --- a/src/protectedApi_v8/contentprivate.ts +++ b/src/protectedApi_v8/contentprivate.ts @@ -15,7 +15,7 @@ const API_END_POINTS = { updateContentEndPoint: (id: string) => `${CONSTANTS.KONG_API_BASE}/private/content/v3/update/${id}`, } // tslint:disable-next-line: no-commented-code -// const editableFields = ['versionKey', 'createdBy', 'creatorContacts'] +const editableFields = ['versionKey', 'createdBy', 'creatorContacts'] const editableFieldsReviewer = ['versionKey', 'isExternal', 'reviewer', 'reviewerIDs'] const editableFieldsPublisher = ['versionKey', 'isExternal', 'publisherIDs: ', 'publisherDetails'] const userIdFailedMessage = 'NO_USER_ID' @@ -37,19 +37,18 @@ contentPrivateApi.patch('/update/:id', async (req, res) => { // tslint: disable - next - line: no - commented - code logInfo('line no: 36 ===> ', id, JSON.stringify(fields), userId, userToken) // tslint: disable - next - line: no - commented - code - // if (fields instanceof Array) { - // for (const entry of fields) { - // if (editableFields.indexOf(entry) === -1) { - // res.status(400).send({ - // msg: FIELD_VALIDATION_ERROR - // }) - // } - // } - // } + if (fields instanceof Array) { + for (const entry of fields) { + if (editableFields.indexOf(entry) === -1) { + res.status(400).send({ + msg: FIELD_VALIDATION_ERROR, + }) + } + } + } // tslint:disable-next-line: no-commented-code const userChannel = await getUserChannel(userToken, userId) const hierarchySource = await getHierarchyDetails(userToken, id) - logInfo('line no: 50 ===> ', userChannel, hierarchySource) if (userChannel !== hierarchySource) { res.status(400).send({ msg: CHANNEL_VALIDATION_ERROR, @@ -95,20 +94,17 @@ contentPrivateApi.patch('/migratereviewer/:id', async (req, res) => { for (const entry of fields) { if (editableFieldsReviewer.indexOf(entry) === -1 && fields.length === editableFieldsReviewer.length) { res.status(400).send({ - msg: res.status(400).send({ msg: FIELD_VALIDATION_ERROR, - }), }) } } } const userChannel = await getUserChannel(userToken, userId) const hierarchySource = await getHierarchyDetails(userToken, id) + logInfo('line no: 50 ===> ', userChannel, hierarchySource) if (userChannel !== hierarchySource) { res.status(400).send({ - msg: res.status(400).send({ - msg: CHANNEL_VALIDATION_ERROR, - }), + msg: CHANNEL_VALIDATION_ERROR, }) } const response = await axios.patch( @@ -123,6 +119,8 @@ contentPrivateApi.patch('/migratereviewer/:id', async (req, res) => { }, } ) + // tslint:disable-next-line: no-commented-code + // logInfo('line no: 70 ===> ', JSON.stringify(response.status), response.data) res.status(response.status).send(response.data) } catch (err) { logError(Error + err) @@ -149,20 +147,17 @@ contentPrivateApi.patch('/migratepublisher/:id', async (req, res) => { for (const entry of fields) { if (editableFieldsPublisher.indexOf(entry) === -1 && fields.length === editableFieldsPublisher.length) { res.status(400).send({ - msg: res.status(400).send({ msg: FIELD_VALIDATION_ERROR, - }), }) } } } const userChannel = await getUserChannel(userToken, userId) const hierarchySource = await getHierarchyDetails(userToken, id) + logInfo('line no: 50 ===> ', userChannel, hierarchySource) if (userChannel !== hierarchySource) { res.status(400).send({ - msg: res.status(400).send({ - msg: CHANNEL_VALIDATION_ERROR, - }), + msg: CHANNEL_VALIDATION_ERROR, }) } const response = await axios.patch( @@ -177,6 +172,8 @@ contentPrivateApi.patch('/migratepublisher/:id', async (req, res) => { }, } ) + // tslint:disable-next-line: no-commented-code + // logInfo('line no: 70 ===> ', JSON.stringify(response.status), response.data) res.status(response.status).send(response.data) } catch (err) { logError(Error + err) From c7ad9c384f893458759bc40b242d537a6d1b7c66 Mon Sep 17 00:00:00 2001 From: "christopher.fernandes" Date: Mon, 24 Jan 2022 12:47:31 +0530 Subject: [PATCH 253/856] Task #0000: Removed logs and logout/reset callback changes --- src/server.ts | 6 +++--- src/utils/apiWhiteList.ts | 7 +------ src/utils/custom-keycloak.ts | 18 +++--------------- src/utils/permissionHelper.ts | 21 ++++----------------- src/utils/proxyCreator.ts | 7 ------- 5 files changed, 11 insertions(+), 48 deletions(-) diff --git a/src/server.ts b/src/server.ts index 8b325748..2126d493 100644 --- a/src/server.ts +++ b/src/server.ts @@ -164,9 +164,9 @@ export class Server { } private resetCookies() { this.app.use('/reset', (_req, res) => { - logInfo('==========================\nCLEARING RES COOKIES') - res.clearCookie('connect.sid') - res.status(200).send() + logInfo('CLEARING RES COOKIES') + res.cookie('connect.sid', '', { expires: new Date() }) + res.redirect('/logout') }) } } diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index ce1bfdf8..5b7adfc9 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -242,7 +242,7 @@ const respond403 = (req: Request, res: Response) => { const respond419 = (req: Request, res: Response) => { const REQ_URL = req.path - if (_.includes(REQ_URL, '/reset')) { + if (_.includes(REQ_URL, '/logout')) { res.send('You are logged out!') } else { const err = ({ msg: 'API WHITELIST :: Unauthorized access for API [ ' + REQ_URL + ' ]', url: REQ_URL }) @@ -364,9 +364,6 @@ const validateAPI = (req: Request, res: Response, next: NextFunction) => { */ export function apiWhiteListLogger() { return (req: Request, res: Response, next: NextFunction) => { - // tslint:disable-next-line: no-console - console.log('apiWhiteListLogger::req.path', req.path , '-- req.query:: ', req.query, '-----cookies---- ', req.cookies, - '------', new Date().toString()) if (req.path === '/' || checkIsStaticRoute(req.path)) { next() return @@ -374,8 +371,6 @@ export function apiWhiteListLogger() { const REQ_URL = req.path if (!_.includes(REQ_URL, '/resource') && (req.session)) { if (!('userRoles' in req.session) || (('userRoles' in req.session) && (req.session.userRoles.length === 0))) { - // tslint:disable-next-line: no-console - console.log('apiWhiteListLogger :: req.session', req.session, '----cookie---', req.cookies, '------', new Date().toString()) logError('Portal_API_WHITELIST_LOGGER: User needs to authenticated themselves', '------', new Date().toString()) respond419(req, res) } else { diff --git a/src/utils/custom-keycloak.ts b/src/utils/custom-keycloak.ts index 84b40d1f..ac92a744 100644 --- a/src/utils/custom-keycloak.ts +++ b/src/utils/custom-keycloak.ts @@ -30,7 +30,7 @@ export class CustomKeycloak { const middleware = composable( keycloak.middleware({ admin: '/callback', - logout: '/reset', + logout: '/logout', }) ) middleware(req, res, next) @@ -59,31 +59,22 @@ export class CustomKeycloak { try { const userId = request.kauth.grant.access_token.content.sub.split(':') request.session.userId = userId[userId.length - 1] - // tslint:disable-next-line: no-console - console.log('userId ::', userId, '------', new Date().toString()) - // tslint:disable-next-line: no-console - console.log('request.session after adding userId ::', request.session, '----cookie---', request.cookies, - '------', new Date().toString()) - + logInfo('userId ::', userId, '------', new Date().toString()) } catch (err) { logError('userId conversation error' + request.kauth.grant.access_token.content.sub, '------', new Date().toString()) } const postLoginRequest = [] // tslint:disable-next-line: no-any postLoginRequest.push((callback: any) => { - // tslint:disable-next-line: no-console - console.log('pushing task to postLoginRequest', '------', new Date().toString()) PERMISSION_HELPER.getCurrentUserRoles(request, callback) }) // tslint:disable-next-line: no-any - async.series(postLoginRequest, (err: any, results: any) => { + async.series(postLoginRequest, (err: any) => { if (err) { logError('error loggin in user', '------', new Date().toString()) next(err, null) } else { - // tslint:disable-next-line: no-console - console.log('async.series results -- ', results, '------', new Date().toString()) logInfo(`${process.pid}: User authenticated`, '------', new Date().toString()) next(null, 'loggedin') } @@ -92,15 +83,12 @@ export class CustomKeycloak { // tslint:disable-next-line: no-any deauthenticated = (request: any) => { - // console.log('De') delete request.session.userRoles delete request.session.userId logInfo(`${process.pid}: User Deauthenticated`) } protect = (req: express.Request, res: express.Response, next: express.NextFunction) => { - // tslint:disable-next-line: no-console - console.log('custom-keycloak::protect start', '------', new Date()) const keycloak = this.getKeyCloakObject(req) return keycloak.protect()(req, res, next) } diff --git a/src/utils/permissionHelper.ts b/src/utils/permissionHelper.ts index 58715f90..2982261f 100644 --- a/src/utils/permissionHelper.ts +++ b/src/utils/permissionHelper.ts @@ -24,9 +24,6 @@ export const PERMISSION_HELPER = { if (!_.includes(reqObj.session.userRoles, 'PUBLIC')) { reqObj.session.userRoles.push('PUBLIC') } - // tslint:disable-next-line: no-console - console.log('request.session after adding userId ::', reqObj.session, '----cookie---', reqObj.cookies, - '------', new Date().toString()) this.createNodeBBUser(reqObj, callback) // tslint:disable-next-line: no-any // reqObj.session.save((error: any) => { @@ -46,12 +43,9 @@ export const PERMISSION_HELPER = { }, // tslint:disable-next-line: no-any setNodeBBUID(reqObj: any, callback: any, body: any) { - logInfo('permissionHelper:: setNodeBBUID function start', '------', new Date().toString()) + logInfo('setNodeBBUID :: ', new Date().toString()) // tslint:disable-next-line: no-any const nodeBBData: any = body - // tslint:disable-next-line: no-console - console.log('request.session after adding userId ::', reqObj.session, '----cookie---', reqObj.cookies, - '------', new Date().toString()) if (reqObj.session) { reqObj.session.uid = nodeBBData.data.result.userId.uid logInfo('After appending uid to session', reqObj.session.uid) @@ -67,11 +61,10 @@ export const PERMISSION_HELPER = { callback(null, nodeBBData) } }) - logInfo('permissionHelper:: setNodeBBUID function end', '------', new Date().toString()) }, // tslint:disable-next-line: no-any getCurrentUserRoles(reqObj: any, callback: any) { - logInfo('Step 3: Get user roles function', '------', new Date().toString()) + logInfo('Step 3: getCurrentUserRoles', '------', new Date().toString()) const userId = reqObj.session.userId const readUrl = `${CONSTANTS.KONG_API_BASE}/user/v2/read/` + userId const options = { @@ -85,15 +78,13 @@ export const PERMISSION_HELPER = { } // tslint:disable-next-line: no-any request.get(options, (err: any, _httpResponse: any, body: any) => { - logInfo('Inside user/v2/read', '------', new Date().toString()) if (body) { // tslint:disable-next-line: no-console - console.log('Success user/v2/read: body', body, '------', new Date().toString()) + logInfo('Success user/v2/read::', '------', new Date().toString()) this.setRolesData(reqObj, callback, body) } if (err) { - // tslint:disable-next-line: no-console - console.log('Making axios call to nodeBB ERROR -- ', err, '------', new Date().toString()) + logError('Making axios call to nodeBB ERROR -- ', err, '------', new Date().toString()) callback(err, null) } }) @@ -109,7 +100,6 @@ export const PERMISSION_HELPER = { identifier: reqObj.session.userId, fullname: reqObj.session.firstName + ' ' + reqObj.session.lastName, } - logInfo('Making axios call to nodeBB', '------', new Date().toString()) try { const nodeBBResp = await axios({ ...axiosRequestConfig, @@ -123,11 +113,8 @@ export const PERMISSION_HELPER = { url: readUrl, }) if (nodeBBResp) { - // tslint:disable-next-line: no-console - console.log('NodeBB axios call response', '------', new Date().toString()) this.setNodeBBUID(reqObj, callback, nodeBBResp) } - logInfo('permissionHelper::createNodeBBUser function end', '------', new Date().toString()) } catch (err) { // tslint:disable-next-line: no-console console.log('Making axios call to nodeBB ERROR -- ', err, '------', new Date().toString()) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 94b848d8..f9f0f631 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -15,8 +15,6 @@ const PROXY_SLUG_WAT = '/proxies/v8/wat' // tslint:disable-next-line: no-any proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { - // tslint:disable-next-line: no-console - console.log('proxyReq::req.cookies=====>', req.cookies) proxyReq.setHeader('X-Channel-Id', (_.get(req, 'session.rootOrgId')) ? _.get(req, 'session.rootOrgId') : CONSTANTS.X_Channel_Id) // tslint:disable-next-line: max-line-length proxyReq.setHeader('Authorization', CONSTANTS.SB_API_KEY) @@ -38,8 +36,6 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { const bodyData = JSON.stringify(req.body) proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData)) proxyReq.write(bodyData) - // tslint:disable-next-line: no-console - console.log('body data=====>', bodyData) } }) @@ -228,9 +224,6 @@ export function proxyCreatorToAppentUserId(route: Router, targetUrl: string, _ti } // tslint:disable-next-line: no-console console.log('REQ_URL_ORIGINAL proxyCreatorToAppentUserId', req.originalUrl) - // tslint:disable-next-line: no-console - console.log('REQ_URL_ORIGINAL -- cookies-- ', req.cookies) - proxy.web(req, res, { changeOrigin: true, ignorePath: true, From e7cf6c70ae05754a22bebfba05f2c3ce7aa3cffc Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Mon, 24 Jan 2022 14:28:02 +0530 Subject: [PATCH 254/856] fixed validation error --- src/protectedApi_v8/contentprivate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protectedApi_v8/contentprivate.ts b/src/protectedApi_v8/contentprivate.ts index 1dd143fa..84f63beb 100644 --- a/src/protectedApi_v8/contentprivate.ts +++ b/src/protectedApi_v8/contentprivate.ts @@ -15,7 +15,7 @@ const API_END_POINTS = { updateContentEndPoint: (id: string) => `${CONSTANTS.KONG_API_BASE}/private/content/v3/update/${id}`, } // tslint:disable-next-line: no-commented-code -const editableFields = ['versionKey', 'createdBy', 'creatorContacts'] +const editableFields = ['versionKey'] const editableFieldsReviewer = ['versionKey', 'isExternal', 'reviewer', 'reviewerIDs'] const editableFieldsPublisher = ['versionKey', 'isExternal', 'publisherIDs: ', 'publisherDetails'] const userIdFailedMessage = 'NO_USER_ID' From 02c9ad2fb87fd4cffcfa2888f2b9925f15e5d616 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Mon, 24 Jan 2022 14:50:52 +0530 Subject: [PATCH 255/856] added proper response --- src/protectedApi_v8/contentprivate.ts | 105 ++++++++++++++------------ 1 file changed, 58 insertions(+), 47 deletions(-) diff --git a/src/protectedApi_v8/contentprivate.ts b/src/protectedApi_v8/contentprivate.ts index 84f63beb..c635e694 100644 --- a/src/protectedApi_v8/contentprivate.ts +++ b/src/protectedApi_v8/contentprivate.ts @@ -39,6 +39,8 @@ contentPrivateApi.patch('/update/:id', async (req, res) => { // tslint: disable - next - line: no - commented - code if (fields instanceof Array) { for (const entry of fields) { + // tslint: disable - next - line: no - commented - code + logInfo('line no: 43 ===> ', entry, JSON.stringify(editableFields)) if (editableFields.indexOf(entry) === -1) { res.status(400).send({ msg: FIELD_VALIDATION_ERROR, @@ -53,22 +55,25 @@ contentPrivateApi.patch('/update/:id', async (req, res) => { res.status(400).send({ msg: CHANNEL_VALIDATION_ERROR, }) - } - const response = await axios.patch( - API_END_POINTS.updateContentEndPoint(id), - req.body, - { - ...axiosRequestConfig, - headers: { - Authorization: CONSTANTS.SB_API_KEY, - // tslint:disable-next-line: all - 'x-authenticated-user-token': userToken, - }, + } else { + const response = await axios.patch( + API_END_POINTS.updateContentEndPoint(id), + req.body, + { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': userToken, + }, + } + ) + // tslint:disable-next-line: no-commented-code + // logInfo('line no: 70 ===> ', JSON.stringify(response.status), response.data) + if (response.status && response.data) { + res.status(response.status).send(response.data) } - ) - // tslint:disable-next-line: no-commented-code - // logInfo('line no: 70 ===> ', JSON.stringify(response.status), response.data) - res.status(response.status).send(response.data) + } } catch (err) { logError(Error + err) res.status((err && err.response && err.response.status) || 500).send( @@ -94,7 +99,7 @@ contentPrivateApi.patch('/migratereviewer/:id', async (req, res) => { for (const entry of fields) { if (editableFieldsReviewer.indexOf(entry) === -1 && fields.length === editableFieldsReviewer.length) { res.status(400).send({ - msg: FIELD_VALIDATION_ERROR, + msg: FIELD_VALIDATION_ERROR, }) } } @@ -106,22 +111,25 @@ contentPrivateApi.patch('/migratereviewer/:id', async (req, res) => { res.status(400).send({ msg: CHANNEL_VALIDATION_ERROR, }) - } - const response = await axios.patch( - API_END_POINTS.updateContentEndPoint(id), - req.body, - { - ...axiosRequestConfig, - headers: { - Authorization: CONSTANTS.SB_API_KEY, - // tslint:disable-next-line: all - 'x-authenticated-user-token': userToken, - }, + } else { + const response = await axios.patch( + API_END_POINTS.updateContentEndPoint(id), + req.body, + { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': userToken, + }, + } + ) + // tslint:disable-next-line: no-commented-code + // logInfo('line no: 70 ===> ', JSON.stringify(response.status), response.data) + if (response.status && response.data) { + res.status(response.status).send(response.data) } - ) - // tslint:disable-next-line: no-commented-code - // logInfo('line no: 70 ===> ', JSON.stringify(response.status), response.data) - res.status(response.status).send(response.data) + } } catch (err) { logError(Error + err) res.status((err && err.response && err.response.status) || 500).send( @@ -147,7 +155,7 @@ contentPrivateApi.patch('/migratepublisher/:id', async (req, res) => { for (const entry of fields) { if (editableFieldsPublisher.indexOf(entry) === -1 && fields.length === editableFieldsPublisher.length) { res.status(400).send({ - msg: FIELD_VALIDATION_ERROR, + msg: FIELD_VALIDATION_ERROR, }) } } @@ -159,22 +167,25 @@ contentPrivateApi.patch('/migratepublisher/:id', async (req, res) => { res.status(400).send({ msg: CHANNEL_VALIDATION_ERROR, }) - } - const response = await axios.patch( - API_END_POINTS.updateContentEndPoint(id), - req.body, - { - ...axiosRequestConfig, - headers: { - Authorization: CONSTANTS.SB_API_KEY, - // tslint:disable-next-line: all - 'x-authenticated-user-token': userToken, - }, + } else { + const response = await axios.patch( + API_END_POINTS.updateContentEndPoint(id), + req.body, + { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': userToken, + }, + } + ) + // tslint:disable-next-line: no-commented-code + // logInfo('line no: 70 ===> ', JSON.stringify(response.status), response.data) + if (response.status && response.data) { + res.status(response.status).send(response.data) } - ) - // tslint:disable-next-line: no-commented-code - // logInfo('line no: 70 ===> ', JSON.stringify(response.status), response.data) - res.status(response.status).send(response.data) + } } catch (err) { logError(Error + err) res.status((err && err.response && err.response.status) || 500).send( From dd20918cf0721a96c23a01c4cd98570877f5d209 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Mon, 24 Jan 2022 15:23:13 +0530 Subject: [PATCH 256/856] added more logs --- src/protectedApi_v8/contentprivate.ts | 44 ++++++++++++++++++--------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/src/protectedApi_v8/contentprivate.ts b/src/protectedApi_v8/contentprivate.ts index c635e694..a3ad463b 100644 --- a/src/protectedApi_v8/contentprivate.ts +++ b/src/protectedApi_v8/contentprivate.ts @@ -15,7 +15,7 @@ const API_END_POINTS = { updateContentEndPoint: (id: string) => `${CONSTANTS.KONG_API_BASE}/private/content/v3/update/${id}`, } // tslint:disable-next-line: no-commented-code -const editableFields = ['versionKey'] +const editableFields = ['versionKey', 'isExternal', 'difficultyLevel', 'visibility'] const editableFieldsReviewer = ['versionKey', 'isExternal', 'reviewer', 'reviewerIDs'] const editableFieldsPublisher = ['versionKey', 'isExternal', 'publisherIDs: ', 'publisherDetails'] const userIdFailedMessage = 'NO_USER_ID' @@ -30,6 +30,7 @@ contentPrivateApi.patch('/update/:id', async (req, res) => { const fields = Object.keys(content) const userId = extractUserId(req) const userToken = extractUserToken(req) as string + let validationErrorFlag = false if (!userId) { res.status(400).send(userIdFailedMessage) return @@ -42,15 +43,19 @@ contentPrivateApi.patch('/update/:id', async (req, res) => { // tslint: disable - next - line: no - commented - code logInfo('line no: 43 ===> ', entry, JSON.stringify(editableFields)) if (editableFields.indexOf(entry) === -1) { - res.status(400).send({ - msg: FIELD_VALIDATION_ERROR, - }) + validationErrorFlag = true } } + if (validationErrorFlag) { + res.status(400).send({ + msg: FIELD_VALIDATION_ERROR, + }) + } } - // tslint:disable-next-line: no-commented-code const userChannel = await getUserChannel(userToken, userId) const hierarchySource = await getHierarchyDetails(userToken, id) + // tslint:disable-next-line: no-commented-code + logInfo('line no: 58 ===> ', JSON.stringify(userChannel), JSON.stringify(hierarchySource)) if (userChannel !== hierarchySource) { res.status(400).send({ msg: CHANNEL_VALIDATION_ERROR, @@ -91,18 +96,22 @@ contentPrivateApi.patch('/migratereviewer/:id', async (req, res) => { const fields = Object.keys(content) const userId = extractUserId(req) const userToken = extractUserToken(req) as string + let validationErrorFlag = false if (!userId) { res.status(400).send(userIdFailedMessage) return } if (fields instanceof Array) { for (const entry of fields) { - if (editableFieldsReviewer.indexOf(entry) === -1 && fields.length === editableFieldsReviewer.length) { - res.status(400).send({ - msg: FIELD_VALIDATION_ERROR, - }) + if (editableFieldsReviewer.indexOf(entry) === -1 && fields.length !== editableFieldsReviewer.length) { + validationErrorFlag = true } } + if (validationErrorFlag) { + res.status(400).send({ + msg: FIELD_VALIDATION_ERROR, + }) + } } const userChannel = await getUserChannel(userToken, userId) const hierarchySource = await getHierarchyDetails(userToken, id) @@ -147,18 +156,23 @@ contentPrivateApi.patch('/migratepublisher/:id', async (req, res) => { const fields = Object.keys(content) const userId = extractUserId(req) const userToken = extractUserToken(req) as string + let validationErrorFlag = false + if (!userId) { res.status(400).send(userIdFailedMessage) return } if (fields instanceof Array) { for (const entry of fields) { - if (editableFieldsPublisher.indexOf(entry) === -1 && fields.length === editableFieldsPublisher.length) { - res.status(400).send({ - msg: FIELD_VALIDATION_ERROR, - }) + if (editableFieldsPublisher.indexOf(entry) === -1 && fields.length !== editableFieldsPublisher.length) { + validationErrorFlag = true } } + if (validationErrorFlag) { + res.status(400).send({ + msg: FIELD_VALIDATION_ERROR, + }) + } } const userChannel = await getUserChannel(userToken, userId) const hierarchySource = await getHierarchyDetails(userToken, id) @@ -208,7 +222,7 @@ export async function getHierarchyDetails(token: string, id: string) { }) logInfo('line 201 ====>', response.data) const hierarchyResult = response.data.result.content - logInfo('line 202 ====>', hierarchyResult) + logInfo('line 202 ====>', JSON.stringify(hierarchyResult)) if (typeof hierarchyResult !== 'undefined' && hierarchyResult != null) { return hierarchyResult.source } @@ -230,7 +244,7 @@ export async function getUserChannel(token: string, userId: string) { }) logInfo('line 222 ====>', response.data) const userProfileResult = response.data.result.response - logInfo('line 222 ====>', userProfileResult) + logInfo('line 222 ====>', JSON.stringify(userProfileResult)) if (typeof userProfileResult !== 'undefined' && userProfileResult != null) { return userProfileResult.channel } From 31d59ea230472f6b59ed23dfd057112eb2dc4393 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Mon, 24 Jan 2022 17:42:31 +0530 Subject: [PATCH 257/856] more changes to fix content read api --- src/protectedApi_v8/contentprivate.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/protectedApi_v8/contentprivate.ts b/src/protectedApi_v8/contentprivate.ts index a3ad463b..6bc46d98 100644 --- a/src/protectedApi_v8/contentprivate.ts +++ b/src/protectedApi_v8/contentprivate.ts @@ -224,7 +224,7 @@ export async function getHierarchyDetails(token: string, id: string) { const hierarchyResult = response.data.result.content logInfo('line 202 ====>', JSON.stringify(hierarchyResult)) if (typeof hierarchyResult !== 'undefined' && hierarchyResult != null) { - return hierarchyResult.source + return hierarchyResult.channel } } catch (error) { logError('ERROR WHILE FETCHING THE Hierarchy DETAILS --> ', error) @@ -246,7 +246,7 @@ export async function getUserChannel(token: string, userId: string) { const userProfileResult = response.data.result.response logInfo('line 222 ====>', JSON.stringify(userProfileResult)) if (typeof userProfileResult !== 'undefined' && userProfileResult != null) { - return userProfileResult.channel + return userProfileResult.rootOrgId } } catch (error) { logError('ERROR WHILE FETCHING THE USER DETAILS --> ', error) From 71196d1d33763e4c633f783a5aca25eed3c9b307 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Tue, 25 Jan 2022 10:59:26 +0530 Subject: [PATCH 258/856] fixes for logout --- src/server.ts | 2 +- src/utils/apiWhiteList.ts | 2 +- src/utils/custom-keycloak.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server.ts b/src/server.ts index 2126d493..705e8ba9 100644 --- a/src/server.ts +++ b/src/server.ts @@ -166,7 +166,7 @@ export class Server { this.app.use('/reset', (_req, res) => { logInfo('CLEARING RES COOKIES') res.cookie('connect.sid', '', { expires: new Date() }) - res.redirect('/logout') + res.redirect('/reset') }) } } diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index 5b7adfc9..74296e6a 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -242,7 +242,7 @@ const respond403 = (req: Request, res: Response) => { const respond419 = (req: Request, res: Response) => { const REQ_URL = req.path - if (_.includes(REQ_URL, '/logout')) { + if (_.includes(REQ_URL, '/reset')) { res.send('You are logged out!') } else { const err = ({ msg: 'API WHITELIST :: Unauthorized access for API [ ' + REQ_URL + ' ]', url: REQ_URL }) diff --git a/src/utils/custom-keycloak.ts b/src/utils/custom-keycloak.ts index ac92a744..6b073ce5 100644 --- a/src/utils/custom-keycloak.ts +++ b/src/utils/custom-keycloak.ts @@ -30,7 +30,7 @@ export class CustomKeycloak { const middleware = composable( keycloak.middleware({ admin: '/callback', - logout: '/logout', + logout: '/reset', }) ) middleware(req, res, next) From 2660e51119afe28e96f510cad1b1f3884a2916ad Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Tue, 25 Jan 2022 15:00:50 +0530 Subject: [PATCH 259/856] added logout html page --- images/Hexagon.png | Bin 0 -> 165198 bytes images/Login_background.png | Bin 0 -> 496890 bytes images/dopt_logo.png | Bin 0 -> 45239 bytes images/igot_karmayogi.svg | 19 ++++++++ index.html | 92 ++++++++++++++++++++++++++++++++++++ src/utils/apiWhiteList.ts | 2 +- 6 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 images/Hexagon.png create mode 100644 images/Login_background.png create mode 100644 images/dopt_logo.png create mode 100644 images/igot_karmayogi.svg create mode 100644 index.html diff --git a/images/Hexagon.png b/images/Hexagon.png new file mode 100644 index 0000000000000000000000000000000000000000..e60e1e5bb4bf9c3e33df537c237e86107df6272d GIT binary patch literal 165198 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%0DHc&{*K9lmZO_9Ag*WE@MNT%vlIWkBuxZ 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

    vZ; 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

    H7^>(xZZDj!|o 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 O&&1H;Y=ypa?Ee8u@cb?S literal 0 HcmV?d00001 diff --git a/images/dopt_logo.png b/images/dopt_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7672d63705e31b73f57f78afc3cafe5712748604 GIT binary patch literal 45239 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 + + igot_karmayogi + + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 00000000..afc11a8d --- /dev/null +++ b/index.html @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + +

    + + \ No newline at end of file diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index 74296e6a..6e3c0582 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -243,7 +243,7 @@ const respond403 = (req: Request, res: Response) => { const respond419 = (req: Request, res: Response) => { const REQ_URL = req.path if (_.includes(REQ_URL, '/reset')) { - res.send('You are logged out!') + res.sendFile('../../index.html') } else { const err = ({ msg: 'API WHITELIST :: Unauthorized access for API [ ' + REQ_URL + ' ]', url: REQ_URL }) logError(err.msg) From 5c739d67182bcf5bf94bddd61627d3dac9a48058 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Tue, 25 Jan 2022 16:25:03 +0530 Subject: [PATCH 260/856] changes in payload --- src/protectedApi_v8/contentprivate.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/protectedApi_v8/contentprivate.ts b/src/protectedApi_v8/contentprivate.ts index 6bc46d98..fdfdb916 100644 --- a/src/protectedApi_v8/contentprivate.ts +++ b/src/protectedApi_v8/contentprivate.ts @@ -26,7 +26,7 @@ contentPrivateApi.patch('/update/:id', async (req, res) => { try { const id = req.params.id // tslint:disable-next-line: no-commented-code - const content = req.body.content + const content = req.body.request.content const fields = Object.keys(content) const userId = extractUserId(req) const userToken = extractUserToken(req) as string @@ -92,7 +92,7 @@ contentPrivateApi.patch('/update/:id', async (req, res) => { contentPrivateApi.patch('/migratereviewer/:id', async (req, res) => { try { const id = req.params.id - const content = req.body.content + const content = req.body.request.content const fields = Object.keys(content) const userId = extractUserId(req) const userToken = extractUserToken(req) as string @@ -152,7 +152,7 @@ contentPrivateApi.patch('/migratereviewer/:id', async (req, res) => { contentPrivateApi.patch('/migratepublisher/:id', async (req, res) => { try { const id = req.params.id - const content = req.body.content + const content = req.body.request.content const fields = Object.keys(content) const userId = extractUserId(req) const userToken = extractUserToken(req) as string From 5db7c88f5a403240ef20a0fc65452cdbdeebb933 Mon Sep 17 00:00:00 2001 From: Nivetha-M <34033831+Nivetha-M@users.noreply.github.com> Date: Tue, 8 Feb 2022 19:55:31 +0530 Subject: [PATCH 261/856] FRAC bookmark & get KR API --- src/protectedApi_v8/frac.ts | 49 ++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/src/protectedApi_v8/frac.ts b/src/protectedApi_v8/frac.ts index a48eeb9b..314c1765 100644 --- a/src/protectedApi_v8/frac.ts +++ b/src/protectedApi_v8/frac.ts @@ -10,10 +10,12 @@ import { extractAuthorizationFromRequest } from '../utils/requestExtract' const API_END_POINTS = { addDataNode: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/addDataNode`, addDataNodeBulk: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/addDataNodeBulk`, + bookmarkDataNode: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/bookmarkDataNode`, filterByMappings: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/filterByMappings`, getActivity: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/getAllNodes?type=ACTIVITY&status=VERIFIED`, getCompetencyArea: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/getAllNodes?type=COMPETENCYAREA`, getDictionary: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/getAllNodes?type=COMPETENCY&status=VERIFIED`, + getKnowledgeResource: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/getAllNodes?type=KNOWLEDGERESOURCE`, getNodeById: (id: string, type: string) => `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/getNodeById?id=${id}&type=${type}&isDetail=true`, getRole: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/getAllNodes?type=ROLE&status=VERIFIED`, @@ -40,6 +42,13 @@ fracApi.get('/getAllNodes/:type', async (req, res) => { case 'activity': apiEndPoint = API_END_POINTS.getActivity break + case 'knowledgeResource': + apiEndPoint = API_END_POINTS.getKnowledgeResource + const queryParams = req.query + if (queryParams.bookmarks) { + apiEndPoint = apiEndPoint + '&bookmarks=' + queryParams.bookmarks + } + break default: res.status(400).send('TYPE_IS_NOT_PROVIDED_OR_TYPE_IS_NOT_CONFIGURED') break @@ -165,21 +174,21 @@ fracApi.get('/:type/:key', async (req, res) => { // tslint:disable-next-line: no-useless-cast const type = req.params.type as string const searchBody = { - childCount : true, + childCount: true, childNodes: true, searches: [ - { - field : 'name', - keyword : key, - type, + { + field: 'name', + keyword: key, + type, }, - { - field : 'status', - keyword : 'VERIFIED', + { + field: 'status', + keyword: 'VERIFIED', type, - }, - ], - } + }, + ], + } logInfo('Req body========>', JSON.stringify(searchBody)) const response = await axios.post(API_END_POINTS.searchNodes, searchBody, { ...axiosRequestConfig, @@ -197,3 +206,21 @@ fracApi.get('/:type/:key', async (req, res) => { ) } }) + +fracApi.post('/bookmarkDataNode', async (req, res) => { + try { + const response = await axios.post(API_END_POINTS.bookmarkDataNode, req.body, { + ...axiosRequestConfig, + headers: { + Authorization: extractAuthorizationFromRequest(req), + }, + }) + res.status(response.status).send(response.data) + } catch (err) { + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: unknownError, + } + ) + } +}) From b31507164f1267e3e2036beee2fe353df1b46035 Mon Sep 17 00:00:00 2001 From: Nivetha-M <34033831+Nivetha-M@users.noreply.github.com> Date: Tue, 8 Feb 2022 19:56:50 +0530 Subject: [PATCH 262/856] FRAC bookmark & get KR API --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index d68bcd48..3db6c794 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1571,6 +1571,13 @@ export const API_LIST = { ROLE.CBP_ADMIN, ], }, + '/protected/v8/frac/bookmarkDataNode': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -1780,5 +1787,6 @@ export const API_LIST = { '/proxies/v8/storage/delete', '/proxies/v8/user/v1/extPatch', '/proxies/v8/learner/course/v2/user/enrollment/admin/list', + '/protected/v8/frac/bookmarkDataNode', ], } From 0f2168dc1d089d557b69a9e4f254a72751c2e836 Mon Sep 17 00:00:00 2001 From: somvitbhowmik <38094153+somvitbhowmik@users.noreply.github.com> Date: Thu, 10 Feb 2022 10:27:13 +0530 Subject: [PATCH 263/856] updated roles for createUser api --- src/utils/whitelistApis.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 3db6c794..62a063f4 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -698,6 +698,8 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.MDO_ADMIN, ROLE.SPV_ADMIN, + ROLE.CBP_ADMIN, + ROLE.CONTENT_CREATOR, ], }, '/protected/v8/user/profileRegistry/getUserRegistryByUser/:id': { From 62317afe34b8394bdfe5179e7fbc42330755e00f Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Thu, 10 Feb 2022 14:11:34 +0530 Subject: [PATCH 264/856] added roles for api --- src/utils/whitelistApis.ts | 438 ++++++++++++++++++------------------- 1 file changed, 217 insertions(+), 221 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 62a063f4..36e3991b 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -43,8 +43,8 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.CONTENT_CREATOR, + ROLE.MDO_ADMIN, + ROLE.CONTENT_CREATOR, ], }, // tslint:disable-next-line: object-literal-sort-keys @@ -348,6 +348,8 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.MDO_ADMIN, ROLE.SPV_ADMIN, + ROLE.CBP_ADMIN, + ROLE.CONTENT_CREATOR ], }, '/proxies/v8/user/v1/search': { @@ -381,6 +383,8 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.MDO_ADMIN, ROLE.SPV_ADMIN, + ROLE.CBP_ADMIN, + ROLE.CONTENT_CREATOR ], }, '/proxies/v8/user/v1/unblock': { @@ -389,6 +393,8 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.MDO_ADMIN, ROLE.SPV_ADMIN, + ROLE.CBP_ADMIN, + ROLE.CONTENT_CREATOR ], }, '/proxies/v8/data/v1/system/settings/get/orgTypeConfig': { @@ -698,8 +704,6 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.MDO_ADMIN, ROLE.SPV_ADMIN, - ROLE.CBP_ADMIN, - ROLE.CONTENT_CREATOR, ], }, '/protected/v8/user/profileRegistry/getUserRegistryByUser/:id': { @@ -1269,7 +1273,7 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/protected/v8/connections/v2/connections/recommended/userDepartment' : { + '/protected/v8/connections/v2/connections/recommended/userDepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -1573,222 +1577,214 @@ export const API_LIST = { ROLE.CBP_ADMIN, ], }, - '/protected/v8/frac/bookmarkDataNode': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, }, URL_PATTERN: - [ - // '/authApi/content/v3/create', - // '/authApi/content/v3/read/:do_id', - // '/authApi/content/v3/update/:do_id', - '/authApi/readBatch/:batchId', - '/authApi/batch/:key', - '/authApi/readCert/:certId', - '/proxies/v8/api/user/v2/read', - '/proxies/v8/api/user/v2/read/:id', - '/proxies/v8/event/v4/read/:do_id', - '/proxies/v8/event/v4/publish/:do_id', - '/proxies/v8/event/v4/create', - '/proxies/v8/user/v1/read/:id', - '/proxies/v8/contentsearch/search', - '/proxies/v8/sunbirdigot/read', - '/proxies/v8/sunbirdigot/search', - '/proxies/v8/discussion/user/v1/create', - '/proxies/v8/data/v1/system/settings/get/orgTypeList', - '/proxies/v8/org/v1/search', - '/proxies/v8/org/v1/update', - '/proxies/v8/notifyContentState', - '/proxies/v8/discussion/topic/:id/:slug', - '/proxies/v8/discussion/v2/posts/:id/vote', - '/proxies/v8/discussion/v2/posts/:id', - '/proxies/v8/discussion/v2/posts/:id/bookmark', - '/proxies/v8/learner/course/v1/user/enrollment/list/:id', - '/proxies/v8/read/content-progres/:do_id', - '/proxies/v8/content-progres/:do_id', - '/proxies/v8/action/content/v3/hierarchy/:do_id', - '/proxies/v8/action/content/v3/hierarchyUpdate', - '/proxies/v8/action/content/v3/hierarchy/update', - '/proxies/v8/action/content/v3/read/:do_id', - '/proxies/v8/discussion/forum/v2/read', - '/proxies/v8/discussion/category/list', - '/proxies/v8/discussion/category/:id', - '/proxies/v8/discussion/forum/tags', - '/proxies/v8/action/content/v3/update/:do_id', - '/proxies/v8/discussion/user/:id', - '/proxies/v8/action/content/v3/create', - '/proxies/v8/upload/action/content/v3/upload/:do_id', - '/proxies/v8/v1/content/retire', - '/proxies/v8/action/content/v3/updateReviewStatus/:do_id', - '/proxies/v8/action/content/v3/review/:do_id', - '/proxies/v8/discussion/recent', - '/proxies/v8/discussion/popular', - '/proxies/v8/action/content/v3/publish/:do_id', - '/proxies/v8/action/content/v3/reject/:do_id', - '/proxies/v8/user/private/v1/assign/role', - '/proxies/v8/user/v1/search', - '/proxies/v8/org/v1/read', - '/proxies/v8/org/v1/create', - '/proxies/v8/discussion/tags', - '/proxies/v8/user/v1/block', - '/proxies/v8/user/v1/unblock', - '/proxies/v8/data/v1/system/settings/get/orgTypeConfig', - '/proxies/v8/learnervm/private/content/v3/publish/:do_id', - '/proxies/v8/learnervm/private/content/v3/review/:do_id', - '/proxies/v8/discussion/v2/topics', - '/proxies/v8/discussion/v2/topics/:id', - '/proxies/v8/discussion/v2/categories', - '/proxies/v8/discussion/tags/:tag', - '/proxies/v8/discussion/user/:userKey/bookmarks', - '/proxies/v8/discussion/user/:userKey/bookmark', - '/proxies/v8/discussion/user/:userKey/upvoted', - '/proxies/v8/discussion/user/:userKey/downvoted', - '/proxies/v8/discussion/categories', - '/proxies/v8/user/v1/autocomplete/:key', - '/proxies/v8/user/v1/migrate', - '/proxies/v8/user/private/v1/migrate', - '/proxies/v8/user/private/v1/assign/role/userrole', - '/proxies/v8/learnervm/private/content/v3/retire/', - '/proxies/v8/private/content/v3/update/:do_id', - '/proxies/v8/dashboard/analytics/getDashboardConfig/Karmayogi/:comp', - '/proxies/v8/dashboard/analytics/getDashboardsForProfile/Karmayogi', - '/proxies/v8/dashboard/analytics/getChartV2/Karmayogi', - '/proxies/v8/wat/dashboard/getDashboardConfig/Karmayogi/overview', - '/protected/v8/user/profileDetails/test', - '/protected/v8/resource/', - '/protected/v8/user/details', - '/protected/v8/connections/connections/recommended/userDepartment', - '/protected/v8/discussionHub/categories/:cid/:slug?/:tid?', - '/protected/v8/discussionHub/topics/recent', - '/protected/v8/cohorts/:cohortType/:contentId', - '/protected/v8/user/content/like', - '/protected/v8/user/telemetry', - '/protected/v8/scrom/get/:id', - '/protected/v8/user/progress/:contentId', - '/protected/v8/user/rating/:contentId', - '/protected/v8/user/progress', - '/protected/v8/user/history/continue', - '/protected/v8/portal/departmentType/', - '/protected/v8/portal/spv/department', - '/protected/v8/portal/spv/deptAction/', - '/protected/v8/user/roles/getUsersV2', - '/protected/v8/portal/spv/mydepartment', - '/protected/v8/portal/mdo/mydepartment', - '/protected/v8/user/profileDetails/createUser', - '/protected/v8/user/profileRegistry/getUserRegistryByUser/:id', - '/protected/v8/user/profileRegistry/createUserRegistryV2', - '/protected/v8/user/profileRegistry/createUserRegistry', - '/protected/v8/portal/deptAction', - '/protected/v8/workflowhandler/historyByApplicationId/:applicationId', - '/protected/v8/user/autocomplete/:query', - '/protected/v8/portal/spv/deptAction/userrole', - '/protected/v8/user/preference', - '/protected/v8/workflowhandler/applicationsSearch', - '/protected/v8/workallocation/getWorkOrders', - '/protected/v8/workallocation/add/workorder', - '/protected/v8/workallocation/getWorkOrderById/:workOrderId', - '/protected/v8/workallocation/user/autocomplete/:searchTerm', - '/protected/v8/workallocation/getUserCompetencies/:userId', - '/protected/v8/frac/searchNodes', - '/protected/v8/frac/filterByMappings', - '/protected/v8/frac/:type/:key', - '/protected/v8/workallocation/v2/add', - '/protected/v8/workallocation/getWorkAllocationById/:workAllocationId', - '/protected/v8/workallocation/update/workorder', - '/protected/v8/workallocation/v2/update', - '/protected/v8/workallocation/getWOPdf/:workOrderId', - '/protected/v8/portal/cbp/mydepartment', - '/protected/v8/user/mandatoryContent/checkStatus', - '/protected/v8/contentprivate/update/:do_id', - '/protected/v8/contentprivate/migratepublisher/:do_id', - '/protected/v8/contentprivate/migratereviewer/:do_id', - '/protected/v8/user/rating/content/average-ratingInfo/:do_id', - '/protected/v8/social/post/timeline', - '/protected/v8/user/history/:id', - '/protected/v8/frac/getAllNodes/:type', - '/protected/v8/frac/getNodeById/:id/:type', - '/protected/v8/portal/listDeptNames', - '/protected/v8/scroing/getTemplate/:templateId', - '/protected/v8/portal/cbc/department', - '/protected/v8/portal/cbc/department/:deptId/', - '/protected/v8/portal/spv/department/:deptId/', - '/protected/v8/scroing/calculate', - // '/protected/v8/connections/connections/recommended', - // '/protected/v8/connections/connections/requests/received', - // '/protected/v8/connections/connections/established', - // '/protected/v8/connections/connections/established/:id', - // '/protected/v8/connections/connections/requested', - // '/protected/v8/connections/add/connection', - // '/protected/v8/connections/connections/suggests', - '/protected/v8/cohorts/course/getUsersForBatch/:courseId', - '/protected/v8/cohorts/user/autoenrollment/:courseId', - '/protected/v8/profanity/startPdfProfanity', - '/protected/v8/profanity/getPdfProfanityForContent/:contentId', - '/protected/v8/catalog', - '/protected/v8/scroing/fetch', - '/protected/v8/portal/mdo/deptAction/userrole', - '/protected/v8/user/profileDetails/updateUser', - '/protected/v8/frac/addDataNodeBulk', - '/protected/v8/roleactivity/:txt', - '/protected/v8/connections/update/connection', - '/protected/v8/workflowhandler/userWFApplicationFieldsSearch', - '/protected/v8/user/details/detailV1', - '/protected/v8/user/profileRegistry/getMasterNationalities', - '/protected/v8/user/profileRegistry/getMasterLanguages', - '/protected/v8/user/profileRegistry/getProfilePageMeta', - '/protected/v8/user/notifications/settings', - '/protected/v8/user/profileRegistry/searchUserRegistry', - '/protected/v8/workflowhandler/transition', - '/protected/v8/workflowhandler/nextActionSearch/:serviceName/:state', - '/protected/v8/workflowhandler/historyByApplicationIdAndWfId/:applicationId/:wfId', - '/protected/v8/workflowhandler/workflowProcess/:wfId', - '/protected/v8/workflowhandler/updateUserProfileWf', - '/protected/v8/workflowhandler/userWfSearch', - '/protected/v8/user/profileRegistry/getUserRegistryById', - '/protected/v8/user/profileRegistry/getUserRegistryById/:id', - '/protected/v8/workallocation/copy/workOrder', - '/reset', - '/protected/v8/user/evaluate/assessment/submit/v2', - '/protected/v8/connections/v2/connections/recommended/userDepartment', - '/protected/v8/connections/v2/connections/recommended', - '/protected/v8/connections/v2/connections/requests/received', - '/protected/v8/connections/v2/connections/established', - '/protected/v8/connections/v2/connections/established/:id', - '/protected/v8/connections/v2/connections/requested', - '/protected/v8/connections/v2/add/connection', - '/protected/v8/connections/v2/connections/suggests', - '/protected/v8/connections/v2/update/connection', - '/protected/v8/user/profileDetails/createUserWithoutInvitationEmail', - '/proxies/v8/searchBy/:key', - '/proxies/v8/staff/position', - '/proxies/v8/staff/position/:orgId', - '/proxies/v8/budget/scheme', - '/proxies/v8/budget/scheme/:orgId', - '/proxies/v8/budget/scheme/:orgId/:budgetYear', - '/proxies/v8/orghistory/:orgId/:key', - '/proxies/v8/discussion/user/uid/:uid', - '/proxies/v8/discussion/user/:username/posts', - '/proxies/v8/action/framework/v3/read/:id', - '/proxies/v8/action/framework/v3/category/master/create', - '/proxies/v8/action/framework/v3/category/master/search', - '/proxies/v8/action/framework/v3/category/master/read/:id', - '/proxies/v8/action/framework/v3/category/create', - '/proxies/v8/action/framework/v3/category/read/:id', - '/proxies/v8/action/framework/v3/category/retire/:id', - '/proxies/v8/action/framework/v3/term/create', - '/proxies/v8/action/content/v3/upload/:do_id', - '/proxies/v8/learner/course/v1/batch/list', - '/protected/v8/cohorts/course/batch/cert/download/:certId', - '/protected/v8/cohorts/course/batch/cert/issue', - '/protected/v8/cohorts/course/batch/cert/template/add', - '/proxies/v8/storage/upload', - '/proxies/v8/storage/delete', - '/proxies/v8/user/v1/extPatch', - '/proxies/v8/learner/course/v2/user/enrollment/admin/list', - '/protected/v8/frac/bookmarkDataNode', - ], + [ + // '/authApi/content/v3/create', + // '/authApi/content/v3/read/:do_id', + // '/authApi/content/v3/update/:do_id', + '/authApi/readBatch/:batchId', + '/authApi/batch/:key', + '/authApi/readCert/:certId', + '/proxies/v8/api/user/v2/read', + '/proxies/v8/api/user/v2/read/:id', + '/proxies/v8/event/v4/read/:do_id', + '/proxies/v8/event/v4/publish/:do_id', + '/proxies/v8/event/v4/create', + '/proxies/v8/user/v1/read/:id', + '/proxies/v8/contentsearch/search', + '/proxies/v8/sunbirdigot/read', + '/proxies/v8/sunbirdigot/search', + '/proxies/v8/discussion/user/v1/create', + '/proxies/v8/data/v1/system/settings/get/orgTypeList', + '/proxies/v8/org/v1/search', + '/proxies/v8/org/v1/update', + '/proxies/v8/notifyContentState', + '/proxies/v8/discussion/topic/:id/:slug', + '/proxies/v8/discussion/v2/posts/:id/vote', + '/proxies/v8/discussion/v2/posts/:id', + '/proxies/v8/discussion/v2/posts/:id/bookmark', + '/proxies/v8/learner/course/v1/user/enrollment/list/:id', + '/proxies/v8/read/content-progres/:do_id', + '/proxies/v8/content-progres/:do_id', + '/proxies/v8/action/content/v3/hierarchy/:do_id', + '/proxies/v8/action/content/v3/hierarchyUpdate', + '/proxies/v8/action/content/v3/hierarchy/update', + '/proxies/v8/action/content/v3/read/:do_id', + '/proxies/v8/discussion/forum/v2/read', + '/proxies/v8/discussion/category/list', + '/proxies/v8/discussion/category/:id', + '/proxies/v8/discussion/forum/tags', + '/proxies/v8/action/content/v3/update/:do_id', + '/proxies/v8/discussion/user/:id', + '/proxies/v8/action/content/v3/create', + '/proxies/v8/upload/action/content/v3/upload/:do_id', + '/proxies/v8/v1/content/retire', + '/proxies/v8/action/content/v3/updateReviewStatus/:do_id', + '/proxies/v8/action/content/v3/review/:do_id', + '/proxies/v8/discussion/recent', + '/proxies/v8/discussion/popular', + '/proxies/v8/action/content/v3/publish/:do_id', + '/proxies/v8/action/content/v3/reject/:do_id', + '/proxies/v8/user/private/v1/assign/role', + '/proxies/v8/user/v1/search', + '/proxies/v8/org/v1/read', + '/proxies/v8/org/v1/create', + '/proxies/v8/discussion/tags', + '/proxies/v8/user/v1/block', + '/proxies/v8/user/v1/unblock', + '/proxies/v8/data/v1/system/settings/get/orgTypeConfig', + '/proxies/v8/learnervm/private/content/v3/publish/:do_id', + '/proxies/v8/learnervm/private/content/v3/review/:do_id', + '/proxies/v8/discussion/v2/topics', + '/proxies/v8/discussion/v2/topics/:id', + '/proxies/v8/discussion/v2/categories', + '/proxies/v8/discussion/tags/:tag', + '/proxies/v8/discussion/user/:userKey/bookmarks', + '/proxies/v8/discussion/user/:userKey/bookmark', + '/proxies/v8/discussion/user/:userKey/upvoted', + '/proxies/v8/discussion/user/:userKey/downvoted', + '/proxies/v8/discussion/categories', + '/proxies/v8/user/v1/autocomplete/:key', + '/proxies/v8/user/v1/migrate', + '/proxies/v8/user/private/v1/migrate', + '/proxies/v8/user/private/v1/assign/role/userrole', + '/proxies/v8/learnervm/private/content/v3/retire/', + '/proxies/v8/private/content/v3/update/:do_id', + '/proxies/v8/dashboard/analytics/getDashboardConfig/Karmayogi/:comp', + '/proxies/v8/dashboard/analytics/getDashboardsForProfile/Karmayogi', + '/proxies/v8/dashboard/analytics/getChartV2/Karmayogi', + '/proxies/v8/wat/dashboard/getDashboardConfig/Karmayogi/overview', + '/protected/v8/user/profileDetails/test', + '/protected/v8/resource/', + '/protected/v8/user/details', + '/protected/v8/connections/connections/recommended/userDepartment', + '/protected/v8/discussionHub/categories/:cid/:slug?/:tid?', + '/protected/v8/discussionHub/topics/recent', + '/protected/v8/cohorts/:cohortType/:contentId', + '/protected/v8/user/content/like', + '/protected/v8/user/telemetry', + '/protected/v8/scrom/get/:id', + '/protected/v8/user/progress/:contentId', + '/protected/v8/user/rating/:contentId', + '/protected/v8/user/progress', + '/protected/v8/user/history/continue', + '/protected/v8/portal/departmentType/', + '/protected/v8/portal/spv/department', + '/protected/v8/portal/spv/deptAction/', + '/protected/v8/user/roles/getUsersV2', + '/protected/v8/portal/spv/mydepartment', + '/protected/v8/portal/mdo/mydepartment', + '/protected/v8/user/profileDetails/createUser', + '/protected/v8/user/profileRegistry/getUserRegistryByUser/:id', + '/protected/v8/user/profileRegistry/createUserRegistryV2', + '/protected/v8/user/profileRegistry/createUserRegistry', + '/protected/v8/portal/deptAction', + '/protected/v8/workflowhandler/historyByApplicationId/:applicationId', + '/protected/v8/user/autocomplete/:query', + '/protected/v8/portal/spv/deptAction/userrole', + '/protected/v8/user/preference', + '/protected/v8/workflowhandler/applicationsSearch', + '/protected/v8/workallocation/getWorkOrders', + '/protected/v8/workallocation/add/workorder', + '/protected/v8/workallocation/getWorkOrderById/:workOrderId', + '/protected/v8/workallocation/user/autocomplete/:searchTerm', + '/protected/v8/workallocation/getUserCompetencies/:userId', + '/protected/v8/frac/searchNodes', + '/protected/v8/frac/filterByMappings', + '/protected/v8/frac/:type/:key', + '/protected/v8/workallocation/v2/add', + '/protected/v8/workallocation/getWorkAllocationById/:workAllocationId', + '/protected/v8/workallocation/update/workorder', + '/protected/v8/workallocation/v2/update', + '/protected/v8/workallocation/getWOPdf/:workOrderId', + '/protected/v8/portal/cbp/mydepartment', + '/protected/v8/user/mandatoryContent/checkStatus', + '/protected/v8/contentprivate/update/:do_id', + '/protected/v8/contentprivate/migratepublisher/:do_id', + '/protected/v8/contentprivate/migratereviewer/:do_id', + '/protected/v8/user/rating/content/average-ratingInfo/:do_id', + '/protected/v8/social/post/timeline', + '/protected/v8/user/history/:id', + '/protected/v8/frac/getAllNodes/:type', + '/protected/v8/frac/getNodeById/:id/:type', + '/protected/v8/portal/listDeptNames', + '/protected/v8/scroing/getTemplate/:templateId', + '/protected/v8/portal/cbc/department', + '/protected/v8/portal/cbc/department/:deptId/', + '/protected/v8/portal/spv/department/:deptId/', + '/protected/v8/scroing/calculate', + // '/protected/v8/connections/connections/recommended', + // '/protected/v8/connections/connections/requests/received', + // '/protected/v8/connections/connections/established', + // '/protected/v8/connections/connections/established/:id', + // '/protected/v8/connections/connections/requested', + // '/protected/v8/connections/add/connection', + // '/protected/v8/connections/connections/suggests', + '/protected/v8/cohorts/course/getUsersForBatch/:courseId', + '/protected/v8/cohorts/user/autoenrollment/:courseId', + '/protected/v8/profanity/startPdfProfanity', + '/protected/v8/profanity/getPdfProfanityForContent/:contentId', + '/protected/v8/catalog', + '/protected/v8/scroing/fetch', + '/protected/v8/portal/mdo/deptAction/userrole', + '/protected/v8/user/profileDetails/updateUser', + '/protected/v8/frac/addDataNodeBulk', + '/protected/v8/roleactivity/:txt', + '/protected/v8/connections/update/connection', + '/protected/v8/workflowhandler/userWFApplicationFieldsSearch', + '/protected/v8/user/details/detailV1', + '/protected/v8/user/profileRegistry/getMasterNationalities', + '/protected/v8/user/profileRegistry/getMasterLanguages', + '/protected/v8/user/profileRegistry/getProfilePageMeta', + '/protected/v8/user/notifications/settings', + '/protected/v8/user/profileRegistry/searchUserRegistry', + '/protected/v8/workflowhandler/transition', + '/protected/v8/workflowhandler/nextActionSearch/:serviceName/:state', + '/protected/v8/workflowhandler/historyByApplicationIdAndWfId/:applicationId/:wfId', + '/protected/v8/workflowhandler/workflowProcess/:wfId', + '/protected/v8/workflowhandler/updateUserProfileWf', + '/protected/v8/workflowhandler/userWfSearch', + '/protected/v8/user/profileRegistry/getUserRegistryById', + '/protected/v8/user/profileRegistry/getUserRegistryById/:id', + '/protected/v8/workallocation/copy/workOrder', + '/reset', + '/protected/v8/user/evaluate/assessment/submit/v2', + '/protected/v8/connections/v2/connections/recommended/userDepartment', + '/protected/v8/connections/v2/connections/recommended', + '/protected/v8/connections/v2/connections/requests/received', + '/protected/v8/connections/v2/connections/established', + '/protected/v8/connections/v2/connections/established/:id', + '/protected/v8/connections/v2/connections/requested', + '/protected/v8/connections/v2/add/connection', + '/protected/v8/connections/v2/connections/suggests', + '/protected/v8/connections/v2/update/connection', + '/protected/v8/user/profileDetails/createUserWithoutInvitationEmail', + '/proxies/v8/searchBy/:key', + '/proxies/v8/staff/position', + '/proxies/v8/staff/position/:orgId', + '/proxies/v8/budget/scheme', + '/proxies/v8/budget/scheme/:orgId', + '/proxies/v8/budget/scheme/:orgId/:budgetYear', + '/proxies/v8/orghistory/:orgId/:key', + '/proxies/v8/discussion/user/uid/:uid', + '/proxies/v8/discussion/user/:username/posts', + '/proxies/v8/action/framework/v3/read/:id', + '/proxies/v8/action/framework/v3/category/master/create', + '/proxies/v8/action/framework/v3/category/master/search', + '/proxies/v8/action/framework/v3/category/master/read/:id', + '/proxies/v8/action/framework/v3/category/create', + '/proxies/v8/action/framework/v3/category/read/:id', + '/proxies/v8/action/framework/v3/category/retire/:id', + '/proxies/v8/action/framework/v3/term/create', + '/proxies/v8/action/content/v3/upload/:do_id', + '/proxies/v8/learner/course/v1/batch/list', + '/protected/v8/cohorts/course/batch/cert/download/:certId', + '/protected/v8/cohorts/course/batch/cert/issue', + '/protected/v8/cohorts/course/batch/cert/template/add', + '/proxies/v8/storage/upload', + '/proxies/v8/storage/delete', + '/proxies/v8/user/v1/extPatch', + '/proxies/v8/learner/course/v2/user/enrollment/admin/list', + ], } From 0e503e1c39037d2fb964d1e253e6304e1f562de5 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Thu, 10 Feb 2022 14:12:42 +0530 Subject: [PATCH 265/856] lint --- src/utils/whitelistApis.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 36e3991b..8fcac7ef 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -349,7 +349,7 @@ export const API_LIST = { ROLE.MDO_ADMIN, ROLE.SPV_ADMIN, ROLE.CBP_ADMIN, - ROLE.CONTENT_CREATOR + ROLE.CONTENT_CREATOR, ], }, '/proxies/v8/user/v1/search': { @@ -384,7 +384,7 @@ export const API_LIST = { ROLE.MDO_ADMIN, ROLE.SPV_ADMIN, ROLE.CBP_ADMIN, - ROLE.CONTENT_CREATOR + ROLE.CONTENT_CREATOR, ], }, '/proxies/v8/user/v1/unblock': { @@ -394,7 +394,7 @@ export const API_LIST = { ROLE.MDO_ADMIN, ROLE.SPV_ADMIN, ROLE.CBP_ADMIN, - ROLE.CONTENT_CREATOR + ROLE.CONTENT_CREATOR, ], }, '/proxies/v8/data/v1/system/settings/get/orgTypeConfig': { From d6878a1df5ff62ee4c304f608eeef4423802fa40 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Thu, 10 Feb 2022 15:07:26 +0530 Subject: [PATCH 266/856] added role --- src/utils/whitelistApis.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 8fcac7ef..98f6ac9f 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -704,6 +704,8 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.MDO_ADMIN, ROLE.SPV_ADMIN, + ROLE.CBP_ADMIN, + ROLE.CONTENT_CREATOR, ], }, '/protected/v8/user/profileRegistry/getUserRegistryByUser/:id': { From 4d45bc5f46bd9f390f230f2d2bcbff15f389c242 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Thu, 10 Feb 2022 18:42:51 +0530 Subject: [PATCH 267/856] Added FRAC bookmarkDataNode API --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 98f6ac9f..96e10eef 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1579,6 +1579,13 @@ export const API_LIST = { ROLE.CBP_ADMIN, ], }, + '/protected/v8/frac/bookmarkDataNode': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -1788,5 +1795,6 @@ export const API_LIST = { '/proxies/v8/storage/delete', '/proxies/v8/user/v1/extPatch', '/proxies/v8/learner/course/v2/user/enrollment/admin/list', + '/protected/v8/frac/bookmarkDataNode' ], } From e4cadaf6e8f6cbf241038cc78ce81fbd1ec7f560 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Thu, 17 Feb 2022 11:05:21 +0530 Subject: [PATCH 268/856] Updated roles --- src/utils/whitelistApis.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 96e10eef..1dce4c0e 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -246,6 +246,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.CONTENT_CREATOR, + ROLE.MDO_ADMIN ], }, '/proxies/v8/dashboard/analytics/getDashboardConfig/Karmayogi/:comp': { From fe737672a75876eb1a7706e8b18bb9f42c71810d Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Thu, 17 Feb 2022 11:21:05 +0530 Subject: [PATCH 269/856] Updated roles --- src/utils/whitelistApis.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 1dce4c0e..2dc403a1 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -283,6 +283,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, ROLE.EDITOR, + ROLE.MDO_ADMIN ], }, '/proxies/v8/v1/content/retire': { From 64561e8709c9d86f0300a4b3cc20aa81f106852f Mon Sep 17 00:00:00 2001 From: Nivetha-M <34033831+Nivetha-M@users.noreply.github.com> Date: Thu, 17 Feb 2022 13:57:57 +0530 Subject: [PATCH 270/856] Added bookmark query params in getNodeById API --- src/protectedApi_v8/frac.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/protectedApi_v8/frac.ts b/src/protectedApi_v8/frac.ts index 314c1765..39c47a50 100644 --- a/src/protectedApi_v8/frac.ts +++ b/src/protectedApi_v8/frac.ts @@ -145,7 +145,12 @@ fracApi.get('/getNodeById/:id/:type', async (req, res) => { try { const id = req.params.id const type = req.params.type - const response = await axios.get(API_END_POINTS.getNodeById(id, type), { + let apiEndpoint = API_END_POINTS.getNodeById(id, type) + const queryParams = req.query + if (queryParams.bookmarks) { + apiEndpoint = apiEndpoint + '&bookmarks=' + queryParams.bookmarks + } + const response = await axios.get(apiEndpoint, { ...axiosRequestConfig, headers: { Authorization: extractAuthorizationFromRequest(req), From e06eee4e520cdc0d221435812ca738b7eae24cfc Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Fri, 18 Feb 2022 12:43:35 +0530 Subject: [PATCH 271/856] added moderation api to whitelis --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 2dc403a1..9d1ece2d 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -554,6 +554,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/apis/proxies/v8/discussion/moderation':{ + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/learnervm/private/content/v3/retire/': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1615,6 +1622,7 @@ export const API_LIST = { '/proxies/v8/discussion/v2/posts/:id/vote', '/proxies/v8/discussion/v2/posts/:id', '/proxies/v8/discussion/v2/posts/:id/bookmark', + '/proxies/v8/discussion/moderation', '/proxies/v8/learner/course/v1/user/enrollment/list/:id', '/proxies/v8/read/content-progres/:do_id', '/proxies/v8/content-progres/:do_id', From a7c546367aacadb4b4fb92eb58fcd450dfd97b14 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Fri, 18 Feb 2022 12:45:00 +0530 Subject: [PATCH 272/856] lint --- src/utils/whitelistApis.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 9d1ece2d..add0e96f 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -246,7 +246,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.CONTENT_CREATOR, - ROLE.MDO_ADMIN + ROLE.MDO_ADMIN, ], }, '/proxies/v8/dashboard/analytics/getDashboardConfig/Karmayogi/:comp': { @@ -283,7 +283,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, ROLE.EDITOR, - ROLE.MDO_ADMIN + ROLE.MDO_ADMIN, ], }, '/proxies/v8/v1/content/retire': { @@ -554,7 +554,7 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/apis/proxies/v8/discussion/moderation':{ + '/apis/proxies/v8/discussion/moderation': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -1805,6 +1805,6 @@ export const API_LIST = { '/proxies/v8/storage/delete', '/proxies/v8/user/v1/extPatch', '/proxies/v8/learner/course/v2/user/enrollment/admin/list', - '/protected/v8/frac/bookmarkDataNode' + '/protected/v8/frac/bookmarkDataNode', ], } From 0192038235279dcda59bd0bdc7677574fc81b182 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Fri, 18 Feb 2022 13:01:39 +0530 Subject: [PATCH 273/856] fixing error --- src/utils/whitelistApis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index add0e96f..c6ea3a55 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -554,7 +554,7 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/apis/proxies/v8/discussion/moderation': { + '/proxies/v8/discussion/moderation': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ From 5fd7eced4d85c9b70a2cb1b95bf7b6f427af7184 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Fri, 18 Feb 2022 20:00:44 +0530 Subject: [PATCH 274/856] Added APIs for assessment --- src/proxies_v8/proxies_v8.ts | 12 ++++++++++++ src/utils/proxyCreator.ts | 28 ++++++++++++++++++++++++++++ src/utils/whitelistApis.ts | 16 ++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 2a2da235..5fddccc2 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -8,6 +8,7 @@ import { logInfo } from '../utils/logger' import { ilpProxyCreatorRoute, // proxyCreatorDiscussion, + proxyAssessmentRead, proxyContent, proxyContentLearnerVM, proxyCreatorKnowledge, @@ -17,6 +18,7 @@ import { proxyCreatorSunbird, proxyCreatorSunbirdSearch, proxyCreatorToAppentUserId, + proxyQuestionRead, scormProxyCreatorRoute } from '../utils/proxyCreator' import { extractUserIdFromRequest, extractUserToken } from '../utils/requestExtract' @@ -293,6 +295,16 @@ proxiesV8.use('/discussion/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) +proxiesV8.use('/assessment/read/*', + // tslint:disable-next-line: max-line-length + proxyAssessmentRead(express.Router(), `${CONSTANTS.KONG_API_BASE}` + '/questionset/v1/hierarchy') +) + +proxiesV8.use('/question/read', + // tslint:disable-next-line: max-line-length + proxyQuestionRead(express.Router(), `${CONSTANTS.KONG_API_BASE}` + '/question/v1/list') +) + function removePrefix(prefix: string, s: string) { return s.substr(prefix.length) } diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index f9f0f631..14c60080 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -275,3 +275,31 @@ export function proxyContentLearnerVM(route: Router, targetUrl: string, _timeout }) return route } + +export function proxyAssessmentRead(route: Router, targetUrl: string, _timeout = 10000): Router { + route.all('/*', (req, res) => { + let url = removePrefix(`${PROXY_SLUG}/assessment/read`, req.originalUrl) + url = targetUrl + url + '?hierarchy=detail' + // tslint:disable-next-line: no-console + console.log('REQ_URL_UPDATED proxyAssessmentRead', url) + proxy.web(req, res, { + changeOrigin: true, + ignorePath: true, + target: url, + }) + }) + return route +} + +export function proxyQuestionRead(route: Router, targetUrl: string, _timeout = 10000): Router { + route.all('/*', (req, res) => { + // tslint:disable-next-line: no-console + console.log('REQ_URL_UPDATED proxyAssessmentRead', targetUrl) + proxy.web(req, res, { + changeOrigin: true, + ignorePath: true, + target: targetUrl, + }) + }) + return route +} diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index c6ea3a55..3b2ea943 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1595,6 +1595,20 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/assessment/read/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/question/read': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -1806,5 +1820,7 @@ export const API_LIST = { '/proxies/v8/user/v1/extPatch', '/proxies/v8/learner/course/v2/user/enrollment/admin/list', '/protected/v8/frac/bookmarkDataNode', + '/proxies/v8/assessment/read/:id', + '/proxies/v8/question/read', ], } From a7dc01ca571341324fcc2e2c47b31cb4b699940c Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Mon, 21 Feb 2022 20:05:25 +0530 Subject: [PATCH 275/856] added api to whitelist --- src/utils/whitelistApis.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 3b2ea943..d1b1958a 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -554,7 +554,14 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/proxies/v8/discussion/moderation': { + '/proxies/v8/discussion/moderation/consumer': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/discussion/moderation/producer': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -1636,7 +1643,8 @@ export const API_LIST = { '/proxies/v8/discussion/v2/posts/:id/vote', '/proxies/v8/discussion/v2/posts/:id', '/proxies/v8/discussion/v2/posts/:id/bookmark', - '/proxies/v8/discussion/moderation', + '/proxies/v8/discussion/moderation/producer', + '/proxies/v8/discussion/moderation/consumer', '/proxies/v8/learner/course/v1/user/enrollment/list/:id', '/proxies/v8/read/content-progres/:do_id', '/proxies/v8/content-progres/:do_id', From 1cfd3dd57d6b4f6bb5fbde6298e4f2d4e7a4c926 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Wed, 23 Feb 2022 15:10:16 +0530 Subject: [PATCH 276/856] Added APIs for Assessment --- src/proxies_v8/proxies_v8.ts | 5 ++++ src/utils/whitelistApis.ts | 49 ++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 5fddccc2..e9708dad 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -305,6 +305,11 @@ proxiesV8.use('/question/read', proxyQuestionRead(express.Router(), `${CONSTANTS.KONG_API_BASE}` + '/question/v1/list') ) +proxiesV8.use('/questionset/*', + // tslint:disable-next-line: max-line-length + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + function removePrefix(prefix: string, s: string) { return s.substr(prefix.length) } diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index d1b1958a..67a340aa 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1616,6 +1616,48 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/questionset/v1/create': { + ROLE_CHECK: [ + ROLE.CONTENT_CREATOR, + ], + checksNeeded: [CHECK.ROLE], + }, + '/proxies/v8/questionset/v1/read': { + ROLE_CHECK: [ + ROLE.CONTENT_CREATOR, + ], + checksNeeded: [CHECK.ROLE], + }, + '/proxies/v8/questionset/v1/update': { + ROLE_CHECK: [ + ROLE.CONTENT_CREATOR, + ], + checksNeeded: [CHECK.ROLE], + }, + '/proxies/v8/questionset/v1/review': { + ROLE_CHECK: [ + ROLE.CONTENT_REVIEWER, + ], + checksNeeded: [CHECK.ROLE], + }, + '/proxies/v8/questionset/v1/publish': { + ROLE_CHECK: [ + ROLE.CONTENT_PUBLISHER, + ], + checksNeeded: [CHECK.ROLE], + }, + '/proxies/v8/questionset/v1/hierarchy/update': { + ROLE_CHECK: [ + ROLE.CONTENT_CREATOR, + ], + checksNeeded: [CHECK.ROLE], + }, + '/proxies/v8/questionset/v1/retire': { + ROLE_CHECK: [ + ROLE.CONTENT_CREATOR, + ], + checksNeeded: [CHECK.ROLE], + }, }, URL_PATTERN: [ @@ -1830,5 +1872,12 @@ export const API_LIST = { '/protected/v8/frac/bookmarkDataNode', '/proxies/v8/assessment/read/:id', '/proxies/v8/question/read', + '/proxies/v8/questionset/v1/create', + '/proxies/v8/questionset/v1/read', + '/proxies/v8/questionset/v1/update', + '/proxies/v8/questionset/v1/review', + '/proxies/v8/questionset/v1/publish', + '/proxies/v8/questionset/v1/hierarchy/update', + '/proxies/v8/questionset/v1/retire', ], } From 436481b581d1b3a467f919c34b86aac29d9acd34 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Wed, 23 Feb 2022 18:52:56 +0530 Subject: [PATCH 277/856] Updated QuestionSet READ API details --- src/utils/whitelistApis.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 67a340aa..72b34819 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1622,9 +1622,11 @@ export const API_LIST = { ], checksNeeded: [CHECK.ROLE], }, - '/proxies/v8/questionset/v1/read': { + '/proxies/v8/questionset/v1/read/:id': { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, + ROLE.CONTENT_PUBLISHER, + ROLE.CONTENT_REVIEWER, ], checksNeeded: [CHECK.ROLE], }, @@ -1873,7 +1875,7 @@ export const API_LIST = { '/proxies/v8/assessment/read/:id', '/proxies/v8/question/read', '/proxies/v8/questionset/v1/create', - '/proxies/v8/questionset/v1/read', + '/proxies/v8/questionset/v1/read/:id', '/proxies/v8/questionset/v1/update', '/proxies/v8/questionset/v1/review', '/proxies/v8/questionset/v1/publish', From 1495d99e855c051bf937e51fe31ec1515723dec1 Mon Sep 17 00:00:00 2001 From: Wilky Singh Date: Wed, 23 Feb 2022 23:42:17 +0530 Subject: [PATCH 278/856] Added APIs for Rating --- src/proxies_v8/proxies_v8.ts | 5 +++++ src/utils/whitelistApis.ts | 30 +++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index e9708dad..0f68aff7 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -310,6 +310,11 @@ proxiesV8.use('/questionset/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) +proxiesV8.use('/ratings/*', + // tslint:disable-next-line: max-line-length + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + function removePrefix(prefix: string, s: string) { return s.substr(prefix.length) } diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 72b34819..6ed3c084 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1660,6 +1660,30 @@ export const API_LIST = { ], checksNeeded: [CHECK.ROLE], }, + '/proxies/v8/ratings/v1/read/:activity_id/:activity_type/:user_id': { + ROLE_CHECK: [ + ROLE.PUBLIC + ], + checksNeeded: [CHECK.ROLE], + }, + '/proxies/v8/ratings/v1/upsert': { + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + checksNeeded: [CHECK.ROLE], + }, + '/proxies/v8/ratings/v1/summary/:activity_id/:activity_type': { + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + checksNeeded: [CHECK.ROLE], + }, + '/proxies/v8/ratings/v1/ratingLookUp': { + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + checksNeeded: [CHECK.ROLE], + }, }, URL_PATTERN: [ @@ -1881,5 +1905,9 @@ export const API_LIST = { '/proxies/v8/questionset/v1/publish', '/proxies/v8/questionset/v1/hierarchy/update', '/proxies/v8/questionset/v1/retire', - ], + '/proxies/v8/ratings/v1/read/:activity_id/:activity_type/:user_id', + '/proxies/v8/ratings/v1/upsert', + '/proxies/v8/ratings/v1/summary/:activity_id/:activity_type', + '/proxies/v8/v1/ratings/v1/ratingLookUp', + ], } From 407219cd9bb9f862c0ef4db35b5e7a51c34bd3dd Mon Sep 17 00:00:00 2001 From: Wilky Singh Date: Thu, 24 Feb 2022 11:02:51 +0530 Subject: [PATCH 279/856] Change incorporated --- src/utils/whitelistApis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 6ed3c084..585a1309 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1908,6 +1908,6 @@ export const API_LIST = { '/proxies/v8/ratings/v1/read/:activity_id/:activity_type/:user_id', '/proxies/v8/ratings/v1/upsert', '/proxies/v8/ratings/v1/summary/:activity_id/:activity_type', - '/proxies/v8/v1/ratings/v1/ratingLookUp', + '/proxies/v8/ratings/v1/ratingLookUp', ], } From 987258b215de8ebbd22353d5764430c683dd288d Mon Sep 17 00:00:00 2001 From: Darshan Nagesh Date: Tue, 1 Mar 2022 20:23:55 +0530 Subject: [PATCH 280/856] Update proxyCreator.ts Adding conditions to include dashboard APIs for Root Org ID Parameter --- src/utils/proxyCreator.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 14c60080..88f37bb4 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -155,6 +155,16 @@ export function proxyCreatorSunbird(route: Router, targetUrl: string, _timeout = // tslint:disable-next-line: no-console console.log('REQ_URL_ORIGINAL proxyCreatorSunbird ======= discussion', url) } + + if (req.originalUrl.includes('/dashboard') && !req.originalUrl.includes('/dashboard/analytics/getChartV2/Karmayogi') && req.session) { + if (req.originalUrl.includes('?')) { + url = `${url}&_uid=${req.rootOrgId}` + } else { + url = `${url}?_uid=${req.rootOrgId}` + } + // tslint:disable-next-line: no-console + console.log('REQ_URL_ORIGINAL proxyCreatorSunbird ======= dashboard analytics', url) + } proxy.web(req, res, { changeOrigin: true, From b56581240eb2011b80eb12b5feef5f92aa7feb2f Mon Sep 17 00:00:00 2001 From: somvitbhowmik <38094153+somvitbhowmik@users.noreply.github.com> Date: Mon, 7 Mar 2022 10:26:54 +0530 Subject: [PATCH 281/856] updated cadre data --- src/static-data/govtOrg.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/static-data/govtOrg.json b/src/static-data/govtOrg.json index 394f717a..ab89f08f 100644 --- a/src/static-data/govtOrg.json +++ b/src/static-data/govtOrg.json @@ -233,6 +233,9 @@ "Central Engineering Service", "Central Electrical. & Mechanical Engineering Service", "Central Architect Service.", - "Central Water Engineering Service" + "Central Water Engineering Service", + "Central Secretariat Service (CSS)", + "Central Secretariat Stenographer's Service (CSSS)", + "Armed Forces Head Quarters Civil Service (AFHQCS)" ] } \ No newline at end of file From d376a1cc52a5adc6a85565205d1140b18e1de8a5 Mon Sep 17 00:00:00 2001 From: "christopher.fernandes" Date: Tue, 8 Mar 2022 12:03:23 +0530 Subject: [PATCH 282/856] added certificate validate whitelist --- src/utils/whitelistApis.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 585a1309..2d8d5d0a 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -160,6 +160,11 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + 'proxies/v8/learner/certreg/v1/certs/validate': { + checksNeeded: [], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [], + }, '/proxies/v8/action/content/v3/hierarchy/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1714,6 +1719,7 @@ export const API_LIST = { '/proxies/v8/discussion/moderation/producer', '/proxies/v8/discussion/moderation/consumer', '/proxies/v8/learner/course/v1/user/enrollment/list/:id', + 'proxies/v8/learner/certreg/v1/certs/validate', '/proxies/v8/read/content-progres/:do_id', '/proxies/v8/content-progres/:do_id', '/proxies/v8/action/content/v3/hierarchy/:do_id', From 23ee1efbc089b99231aad339c2381df387a58dbd Mon Sep 17 00:00:00 2001 From: "christopher.fernandes" Date: Tue, 8 Mar 2022 12:18:34 +0530 Subject: [PATCH 283/856] Task #0000: lint fixes for build --- src/utils/proxyCreator.ts | 9 +++++---- src/utils/whitelistApis.ts | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 88f37bb4..efd59794 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -15,6 +15,7 @@ const PROXY_SLUG_WAT = '/proxies/v8/wat' // tslint:disable-next-line: no-any proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { + // tslint:disable-next-line: no-duplicate-string proxyReq.setHeader('X-Channel-Id', (_.get(req, 'session.rootOrgId')) ? _.get(req, 'session.rootOrgId') : CONSTANTS.X_Channel_Id) // tslint:disable-next-line: max-line-length proxyReq.setHeader('Authorization', CONSTANTS.SB_API_KEY) @@ -134,7 +135,7 @@ export function proxyCreatorLearner(route: Router, targetUrl: string, _timeout = }) return route } - +// tslint:disable-next-line export function proxyCreatorSunbird(route: Router, targetUrl: string, _timeout = 10000): Router { route.all('/*', (req, res) => { // tslint:disable-next-line: no-console @@ -155,12 +156,12 @@ export function proxyCreatorSunbird(route: Router, targetUrl: string, _timeout = // tslint:disable-next-line: no-console console.log('REQ_URL_ORIGINAL proxyCreatorSunbird ======= discussion', url) } - + if (req.originalUrl.includes('/dashboard') && !req.originalUrl.includes('/dashboard/analytics/getChartV2/Karmayogi') && req.session) { if (req.originalUrl.includes('?')) { - url = `${url}&_uid=${req.rootOrgId}` + url = `${url}&_uid=${_.get(req, 'session.rootOrgId')}` } else { - url = `${url}?_uid=${req.rootOrgId}` + url = `${url}?_uid=${_.get(req, 'session.rootOrgId')}` } // tslint:disable-next-line: no-console console.log('REQ_URL_ORIGINAL proxyCreatorSunbird ======= dashboard analytics', url) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 2d8d5d0a..63264256 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1667,7 +1667,7 @@ export const API_LIST = { }, '/proxies/v8/ratings/v1/read/:activity_id/:activity_type/:user_id': { ROLE_CHECK: [ - ROLE.PUBLIC + ROLE.PUBLIC, ], checksNeeded: [CHECK.ROLE], }, From 17aceb5429f398a415eb755f32b39f2a2488fa62 Mon Sep 17 00:00:00 2001 From: "christopher.fernandes" Date: Wed, 9 Mar 2022 11:10:42 +0530 Subject: [PATCH 284/856] Task #57573: added certificate validate whitelist --- src/utils/whitelistApis.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 63264256..4bdab89e 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -160,7 +160,7 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - 'proxies/v8/learner/certreg/v1/certs/validate': { + '/proxies/v8/learner/certreg/v1/certs/validate': { checksNeeded: [], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [], @@ -1719,7 +1719,7 @@ export const API_LIST = { '/proxies/v8/discussion/moderation/producer', '/proxies/v8/discussion/moderation/consumer', '/proxies/v8/learner/course/v1/user/enrollment/list/:id', - 'proxies/v8/learner/certreg/v1/certs/validate', + '/proxies/v8/learner/certreg/v1/certs/validate', '/proxies/v8/read/content-progres/:do_id', '/proxies/v8/content-progres/:do_id', '/proxies/v8/action/content/v3/hierarchy/:do_id', From 14f133988969333ee348e05dc5b58a3c9c4e36c9 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Wed, 9 Mar 2022 15:42:44 +0530 Subject: [PATCH 285/856] Added API for QuestionSet Hierarchy read --- src/utils/whitelistApis.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 4bdab89e..fd7d15cc 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1665,6 +1665,12 @@ export const API_LIST = { ], checksNeeded: [CHECK.ROLE], }, + '/proxies/v8/questionset/v1/hierarchy/:id': { + ROLE_CHECK: [ + ROLE.CONTENT_CREATOR, + ], + checksNeeded: [CHECK.ROLE], + }, '/proxies/v8/ratings/v1/read/:activity_id/:activity_type/:user_id': { ROLE_CHECK: [ ROLE.PUBLIC, @@ -1911,6 +1917,7 @@ export const API_LIST = { '/proxies/v8/questionset/v1/publish', '/proxies/v8/questionset/v1/hierarchy/update', '/proxies/v8/questionset/v1/retire', + '/proxies/v8/questionset/v1/hierarchy/:id', '/proxies/v8/ratings/v1/read/:activity_id/:activity_type/:user_id', '/proxies/v8/ratings/v1/upsert', '/proxies/v8/ratings/v1/summary/:activity_id/:activity_type', From 8ff3f115609546b81dfe1717adc312ca47e317bb Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Tue, 15 Mar 2022 13:34:32 +0530 Subject: [PATCH 286/856] Added API to read question for CBP --- src/proxies_v8/proxies_v8.ts | 5 +++++ src/utils/whitelistApis.ts | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 0f68aff7..392eeb77 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -305,6 +305,11 @@ proxiesV8.use('/question/read', proxyQuestionRead(express.Router(), `${CONSTANTS.KONG_API_BASE}` + '/question/v1/list') ) +proxiesV8.use('/cbp/question/list', + // tslint:disable-next-line: max-line-length + proxyQuestionRead(express.Router(), `${CONSTANTS.KONG_API_BASE}` + '/question/v1/list') +) + proxiesV8.use('/questionset/*', // tslint:disable-next-line: max-line-length proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index fd7d15cc..cb1e574e 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1621,6 +1621,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/cbp/question/list': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.CONTENT_CREATOR, + ], + }, '/proxies/v8/questionset/v1/create': { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, @@ -1910,6 +1917,7 @@ export const API_LIST = { '/protected/v8/frac/bookmarkDataNode', '/proxies/v8/assessment/read/:id', '/proxies/v8/question/read', + '/proxies/v8/cbp/question/list', '/proxies/v8/questionset/v1/create', '/proxies/v8/questionset/v1/read/:id', '/proxies/v8/questionset/v1/update', From 28cac9af72c72e44d3052df85159eb3112eb0e06 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Wed, 16 Mar 2022 15:54:59 +0530 Subject: [PATCH 287/856] Updated the roles required for questionset Review API --- src/utils/whitelistApis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index cb1e574e..e906aa51 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1650,7 +1650,7 @@ export const API_LIST = { }, '/proxies/v8/questionset/v1/review': { ROLE_CHECK: [ - ROLE.CONTENT_REVIEWER, + ROLE.CONTENT_CREATOR, ], checksNeeded: [CHECK.ROLE], }, From 5cc74c97951ec52b0b6f55195e78fe2c9784f994 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Wed, 16 Mar 2022 18:03:19 +0530 Subject: [PATCH 288/856] Updated QuestionSet API path --- src/utils/whitelistApis.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index e906aa51..4bc80e77 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1648,13 +1648,13 @@ export const API_LIST = { ], checksNeeded: [CHECK.ROLE], }, - '/proxies/v8/questionset/v1/review': { + '/proxies/v8/questionset/v1/review/:id': { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, ], checksNeeded: [CHECK.ROLE], }, - '/proxies/v8/questionset/v1/publish': { + '/proxies/v8/questionset/v1/publish/:id': { ROLE_CHECK: [ ROLE.CONTENT_PUBLISHER, ], @@ -1921,8 +1921,8 @@ export const API_LIST = { '/proxies/v8/questionset/v1/create', '/proxies/v8/questionset/v1/read/:id', '/proxies/v8/questionset/v1/update', - '/proxies/v8/questionset/v1/review', - '/proxies/v8/questionset/v1/publish', + '/proxies/v8/questionset/v1/review/:id', + '/proxies/v8/questionset/v1/publish/:id', '/proxies/v8/questionset/v1/hierarchy/update', '/proxies/v8/questionset/v1/retire', '/proxies/v8/questionset/v1/hierarchy/:id', From 7db05cde154abb2f2ac71714c6135af441d2a40a Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Wed, 16 Mar 2022 18:28:30 +0530 Subject: [PATCH 289/856] Added roles for QuestionSet APIs --- src/utils/whitelistApis.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 4bc80e77..8cbb49cd 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1642,9 +1642,11 @@ export const API_LIST = { ], checksNeeded: [CHECK.ROLE], }, - '/proxies/v8/questionset/v1/update': { + '/proxies/v8/questionset/v1/update/:id': { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, + ROLE.CONTENT_PUBLISHER, + ROLE.CONTENT_REVIEWER, ], checksNeeded: [CHECK.ROLE], }, @@ -1675,6 +1677,8 @@ export const API_LIST = { '/proxies/v8/questionset/v1/hierarchy/:id': { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, + ROLE.CONTENT_PUBLISHER, + ROLE.CONTENT_REVIEWER, ], checksNeeded: [CHECK.ROLE], }, @@ -1920,7 +1924,7 @@ export const API_LIST = { '/proxies/v8/cbp/question/list', '/proxies/v8/questionset/v1/create', '/proxies/v8/questionset/v1/read/:id', - '/proxies/v8/questionset/v1/update', + '/proxies/v8/questionset/v1/update/:id', '/proxies/v8/questionset/v1/review/:id', '/proxies/v8/questionset/v1/publish/:id', '/proxies/v8/questionset/v1/hierarchy/update', From cdb2594ff671367bcd325ff59f35e375f41e7515 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Wed, 16 Mar 2022 23:33:56 +0530 Subject: [PATCH 290/856] Added roles to Question List API --- src/utils/whitelistApis.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 8cbb49cd..5f703495 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1626,6 +1626,8 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.CONTENT_CREATOR, + ROLE.CONTENT_PUBLISHER, + ROLE.CONTENT_REVIEWER, ], }, '/proxies/v8/questionset/v1/create': { From 564271a8e4e3b01e4d66e364a4faa6f65f5db845 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Tue, 22 Mar 2022 05:14:39 +0530 Subject: [PATCH 291/856] Using FRAC local instead of public domain --- src/protectedApi_v8/frac.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/protectedApi_v8/frac.ts b/src/protectedApi_v8/frac.ts index 39c47a50..de106583 100644 --- a/src/protectedApi_v8/frac.ts +++ b/src/protectedApi_v8/frac.ts @@ -8,18 +8,18 @@ import { ERROR } from '../utils/message' import { extractAuthorizationFromRequest } from '../utils/requestExtract' const API_END_POINTS = { - addDataNode: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/addDataNode`, - addDataNodeBulk: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/addDataNodeBulk`, - bookmarkDataNode: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/bookmarkDataNode`, - filterByMappings: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/filterByMappings`, - getActivity: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/getAllNodes?type=ACTIVITY&status=VERIFIED`, - getCompetencyArea: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/getAllNodes?type=COMPETENCYAREA`, - getDictionary: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/getAllNodes?type=COMPETENCY&status=VERIFIED`, - getKnowledgeResource: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/getAllNodes?type=KNOWLEDGERESOURCE`, + addDataNode: `${CONSTANTS.FRAC_API_BASE}/frac/addDataNode`, + addDataNodeBulk: `${CONSTANTS.FRAC_API_BASE}/frac/addDataNodeBulk`, + bookmarkDataNode: `${CONSTANTS.FRAC_API_BASE}/frac/bookmarkDataNode`, + filterByMappings: `${CONSTANTS.FRAC_API_BASE}/frac/filterByMappings`, + getActivity: `${CONSTANTS.FRAC_API_BASE}/frac/getAllNodes?type=ACTIVITY&status=VERIFIED`, + getCompetencyArea: `${CONSTANTS.FRAC_API_BASE}/frac/getAllNodes?type=COMPETENCYAREA`, + getDictionary: `${CONSTANTS.FRAC_API_BASE}/frac/getAllNodes?type=COMPETENCY&status=VERIFIED`, + getKnowledgeResource: `${CONSTANTS.FRAC_API_BASE}/frac/getAllNodes?type=KNOWLEDGERESOURCE`, getNodeById: (id: string, type: string) => - `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/getNodeById?id=${id}&type=${type}&isDetail=true`, - getRole: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/getAllNodes?type=ROLE&status=VERIFIED`, - searchNodes: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/searchNodes`, + `${CONSTANTS.FRAC_API_BASE}/frac/getNodeById?id=${id}&type=${type}&isDetail=true`, + getRole: `${CONSTANTS.FRAC_API_BASE}/frac/getAllNodes?type=ROLE&status=VERIFIED`, + searchNodes: `${CONSTANTS.FRAC_API_BASE}/frac/searchNodes`, } export const fracApi = Router() From 408019694970840f824cc64772423acceade5fcc Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Wed, 23 Mar 2022 17:04:34 +0530 Subject: [PATCH 292/856] Fixed issues in retire API for questionSet --- src/utils/whitelistApis.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 5f703495..5d9d8941 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1670,9 +1670,11 @@ export const API_LIST = { ], checksNeeded: [CHECK.ROLE], }, - '/proxies/v8/questionset/v1/retire': { + '/proxies/v8/questionset/v1/retire/:id': { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, + ROLE.CONTENT_PUBLISHER, + ROLE.CONTENT_REVIEWER, ], checksNeeded: [CHECK.ROLE], }, @@ -1930,7 +1932,7 @@ export const API_LIST = { '/proxies/v8/questionset/v1/review/:id', '/proxies/v8/questionset/v1/publish/:id', '/proxies/v8/questionset/v1/hierarchy/update', - '/proxies/v8/questionset/v1/retire', + '/proxies/v8/questionset/v1/retire/:id', '/proxies/v8/questionset/v1/hierarchy/:id', '/proxies/v8/ratings/v1/read/:activity_id/:activity_type/:user_id', '/proxies/v8/ratings/v1/upsert', From 1d6e0a1d00764a06d4a99d05c6e235bb34daa7c9 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 24 Mar 2022 13:34:36 +0530 Subject: [PATCH 293/856] Added assessment submit API --- src/protectedApi_v8/user/evaluate.ts | 29 ++++++++++++++++++++++++++++ src/utils/whitelistApis.ts | 8 ++++++++ 2 files changed, 37 insertions(+) diff --git a/src/protectedApi_v8/user/evaluate.ts b/src/protectedApi_v8/user/evaluate.ts index 4ff1114d..1ef22bae 100644 --- a/src/protectedApi_v8/user/evaluate.ts +++ b/src/protectedApi_v8/user/evaluate.ts @@ -8,6 +8,7 @@ import { extractUserIdFromRequest, extractUserToken } from '../../utils/requestE const GENERAL_ERR_MSG = 'Failed due to unknown reason' const API_END_POINTS = { assessmentSubmitV2: `${CONSTANTS.KONG_API_BASE}/v2/user`, + assessmentSubmitV3: `${CONSTANTS.KONG_API_BASE}/v3/user`, iapSubmitAssessment: `${CONSTANTS.SB_EXT_API_BASE_2}/v3/iap-assessment`, postAssessment: `${CONSTANTS.POST_ASSESSMENT_BASE}/lmsapi/v1/post_assessment`, } @@ -48,6 +49,34 @@ evaluateApi.post('/assessment/submit/v2', async (req, res) => { } }) +evaluateApi.post('/assessment/submit/v3', async (req, res) => { + try { + const userId = extractUserIdFromRequest(req) + const url = `${API_END_POINTS.assessmentSubmitV2}/assessment/submit` + const requestBody = { + ...req.body, + } + const response = await axios({ + ...axiosRequestConfig, + data: requestBody, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + userId, + 'x-authenticated-user-token': extractUserToken(req), + }, + method: 'POST', + url, + }) + res.status(response.status).send(response.data) + } catch (err) { + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: GENERAL_ERR_MSG, + } + ) + } +}) + evaluateApi.post('/assessment/submit/iap', async (req, res) => { const url = `${API_END_POINTS.iapSubmitAssessment}` const requestBody = { diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 5d9d8941..0fe2919d 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1296,6 +1296,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/user/evaluate/assessment/submit/v3': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + } '/protected/v8/connections/v2/connections/recommended/userDepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1886,6 +1893,7 @@ export const API_LIST = { '/protected/v8/workallocation/copy/workOrder', '/reset', '/protected/v8/user/evaluate/assessment/submit/v2', + '/protected/v8/user/evaluate/assessment/submit/v3', '/protected/v8/connections/v2/connections/recommended/userDepartment', '/protected/v8/connections/v2/connections/recommended', '/protected/v8/connections/v2/connections/requests/received', From 08e47da50ea35c0df551771ce1ed2518c806d3a9 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 24 Mar 2022 13:48:59 +0530 Subject: [PATCH 294/856] Updated frac API path --- src/utils/whitelistApis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 0fe2919d..b96c2664 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1302,7 +1302,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.PUBLIC, ], - } + }, '/protected/v8/connections/v2/connections/recommended/userDepartment': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys From 76e9a913b8ad0f1fd3a7899dd33ae5d577a3650d Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 24 Mar 2022 13:54:10 +0530 Subject: [PATCH 295/856] Updated frac API path --- src/protectedApi_v8/roleActivity.ts | 2 +- src/protectedApi_v8/user/profile-registry.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/protectedApi_v8/roleActivity.ts b/src/protectedApi_v8/roleActivity.ts index fb03b6e5..0196daf3 100644 --- a/src/protectedApi_v8/roleActivity.ts +++ b/src/protectedApi_v8/roleActivity.ts @@ -11,7 +11,7 @@ import { extractAuthorizationFromRequest } from '../utils/requestExtract' export const roleActivityApi = Router() const API_END_POINTS = { - searchNodes: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/searchNodes`, + searchNodes: `${CONSTANTS.FRAC_API_BASE}/frac/searchNodes`, } roleActivityApi.get('/', async (req, res) => { diff --git a/src/protectedApi_v8/user/profile-registry.ts b/src/protectedApi_v8/user/profile-registry.ts index 0a3bbf01..179e4c1a 100644 --- a/src/protectedApi_v8/user/profile-registry.ts +++ b/src/protectedApi_v8/user/profile-registry.ts @@ -10,7 +10,7 @@ const fs = require('fs') const API_END_POINTS = { createUserRegistry: (userId: string) => `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/create/profile?userId=${userId}`, - getAllPosition: `${CONSTANTS.FRAC_API_BASE}/fracapis/frac/getAllNodes?type=POSITION&status=VERIFIED`, + getAllPosition: `${CONSTANTS.FRAC_API_BASE}/frac/getAllNodes?type=POSITION&status=VERIFIED`, getUserRegistry: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/get/profile`, getUserRegistryById: (userId: string) => `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/search/profile?userId=${userId}`, searchUserRegistry: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/search/profile`, From e5fee6c36cee578037116346125c84fbdd32f162 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 24 Mar 2022 14:07:09 +0530 Subject: [PATCH 296/856] Updated typo error in assessment API --- src/protectedApi_v8/user/evaluate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protectedApi_v8/user/evaluate.ts b/src/protectedApi_v8/user/evaluate.ts index 1ef22bae..142ed573 100644 --- a/src/protectedApi_v8/user/evaluate.ts +++ b/src/protectedApi_v8/user/evaluate.ts @@ -52,7 +52,7 @@ evaluateApi.post('/assessment/submit/v2', async (req, res) => { evaluateApi.post('/assessment/submit/v3', async (req, res) => { try { const userId = extractUserIdFromRequest(req) - const url = `${API_END_POINTS.assessmentSubmitV2}/assessment/submit` + const url = `${API_END_POINTS.assessmentSubmitV3}/assessment/submit` const requestBody = { ...req.body, } From cfe2f130b25d1700015ef9c561a9cf74d77bc717 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 24 Mar 2022 16:23:58 +0530 Subject: [PATCH 297/856] Updated path for hierarchy read and question list in assessment API --- src/proxies_v8/proxies_v8.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 392eeb77..bdb76c8a 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -297,12 +297,12 @@ proxiesV8.use('/discussion/*', proxiesV8.use('/assessment/read/*', // tslint:disable-next-line: max-line-length - proxyAssessmentRead(express.Router(), `${CONSTANTS.KONG_API_BASE}` + '/questionset/v1/hierarchy') + proxyAssessmentRead(express.Router(), `${CONSTANTS.KONG_API_BASE}` + '/player/questionset/v1/hierarchy') ) proxiesV8.use('/question/read', // tslint:disable-next-line: max-line-length - proxyQuestionRead(express.Router(), `${CONSTANTS.KONG_API_BASE}` + '/question/v1/list') + proxyQuestionRead(express.Router(), `${CONSTANTS.KONG_API_BASE}` + '/player/question/v1/list') ) proxiesV8.use('/cbp/question/list', From 91baab7f4bf88b2a103521b23935aa7689971201 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Tue, 29 Mar 2022 10:24:27 +0530 Subject: [PATCH 298/856] Added QuestionSet Reject APIs --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index b96c2664..bead5425 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1685,6 +1685,13 @@ export const API_LIST = { ], checksNeeded: [CHECK.ROLE], }, + '/proxies/v8/questionset/v1/reject/:id': { + ROLE_CHECK: [ + ROLE.CONTENT_PUBLISHER, + ROLE.CONTENT_REVIEWER, + ], + checksNeeded: [CHECK.ROLE], + }, '/proxies/v8/questionset/v1/hierarchy/:id': { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, @@ -1941,6 +1948,7 @@ export const API_LIST = { '/proxies/v8/questionset/v1/publish/:id', '/proxies/v8/questionset/v1/hierarchy/update', '/proxies/v8/questionset/v1/retire/:id', + '/proxies/v8/questionset/v1/reject/:id' '/proxies/v8/questionset/v1/hierarchy/:id', '/proxies/v8/ratings/v1/read/:activity_id/:activity_type/:user_id', '/proxies/v8/ratings/v1/upsert', From af059f11baa4a68f6a7eaaff02df13c7b3ed1e15 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Tue, 29 Mar 2022 10:50:09 +0530 Subject: [PATCH 299/856] Fixed syntax issue for questionset API --- src/utils/whitelistApis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index bead5425..b9bba53f 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1948,7 +1948,7 @@ export const API_LIST = { '/proxies/v8/questionset/v1/publish/:id', '/proxies/v8/questionset/v1/hierarchy/update', '/proxies/v8/questionset/v1/retire/:id', - '/proxies/v8/questionset/v1/reject/:id' + '/proxies/v8/questionset/v1/reject/:id', '/proxies/v8/questionset/v1/hierarchy/:id', '/proxies/v8/ratings/v1/read/:activity_id/:activity_type/:user_id', '/proxies/v8/ratings/v1/upsert', From b862da21556bc3e5215f341419aa92ebfd2f76d8 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Tue, 29 Mar 2022 11:01:10 +0530 Subject: [PATCH 300/856] added email in payload --- src/utils/proxyCreator.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index efd59794..c76dcd06 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -1,6 +1,6 @@ import { Router } from 'express' import { createProxyServer } from 'http-proxy' -import { extractUserIdFromRequest, extractUserToken } from '../utils/requestExtract' +import { extractUserEmailFromRequest, extractUserIdFromRequest, extractUserToken } from '../utils/requestExtract' import { CONSTANTS } from './env' import { logInfo } from './logger' @@ -153,6 +153,9 @@ export function proxyCreatorSunbird(route: Router, targetUrl: string, _timeout = } else { url = `${url}?_uid=${req.session.uid}` } + if(req.originalUrl.includes('/discussion/v2/topics')){ + req.body.email = extractUserEmailFromRequest(req) + } // tslint:disable-next-line: no-console console.log('REQ_URL_ORIGINAL proxyCreatorSunbird ======= discussion', url) } From c9632bbebd3ea7a82280392ebaee7c364af26af4 Mon Sep 17 00:00:00 2001 From: arunpilli21 Date: Tue, 29 Mar 2022 11:03:04 +0530 Subject: [PATCH 301/856] lint fix --- src/utils/proxyCreator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index c76dcd06..01fba08b 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -153,7 +153,7 @@ export function proxyCreatorSunbird(route: Router, targetUrl: string, _timeout = } else { url = `${url}?_uid=${req.session.uid}` } - if(req.originalUrl.includes('/discussion/v2/topics')){ + if (req.originalUrl.includes('/discussion/v2/topics')) { req.body.email = extractUserEmailFromRequest(req) } // tslint:disable-next-line: no-console From d283d5a29c40da4c34d5c8e2884796b074c0d739 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 7 Apr 2022 00:56:51 +0530 Subject: [PATCH 302/856] fix for user read api --- src/protectedApi_v8/user/profile-details.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protectedApi_v8/user/profile-details.ts b/src/protectedApi_v8/user/profile-details.ts index f1d59d52..fa6a41a6 100644 --- a/src/protectedApi_v8/user/profile-details.ts +++ b/src/protectedApi_v8/user/profile-details.ts @@ -291,7 +291,7 @@ profileDeatailsApi.post('/createUser', async (req, res) => { method: 'GET', url: API_END_POINTS.kongUserRead(sbUserId), }) - if (sbUserReadResponse.data.params.status !== 'success') { + if (sbUserReadResponse.data.params.status !== 'SUCCESS') { res.status(500).send(failedToReadUser) return } From 954dd2ca20f54ce66f01426d59271abf2046ae8b Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 7 Apr 2022 01:01:09 +0530 Subject: [PATCH 303/856] fix for user create api --- src/protectedApi_v8/user/profile-details.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/protectedApi_v8/user/profile-details.ts b/src/protectedApi_v8/user/profile-details.ts index fa6a41a6..a4597a82 100644 --- a/src/protectedApi_v8/user/profile-details.ts +++ b/src/protectedApi_v8/user/profile-details.ts @@ -369,7 +369,7 @@ profileDeatailsApi.post('/createUser', async (req, res) => { }) logInfo('Received response from password reset -> ' + passwordResetResponse) - if (passwordResetResponse.data.params.status === 'success') { + if (passwordResetResponse.data.params.status === 'SUCCESS') { const welcomeMailRequest = { allowedLoging: 'You can use your email to Login', body: 'Hello', @@ -394,7 +394,7 @@ profileDeatailsApi.post('/createUser', async (req, res) => { url: API_END_POINTS.kongSendWelcomeEmail, }) - if (welcomeMailResponse.data.params.status !== 'success') { + if (welcomeMailResponse.data.params.status !== 'SUCCESS') { res.status(500).send('Failed to send Welcome Email.') return } @@ -576,7 +576,7 @@ profileDeatailsApi.post('/createUserV2WithoutRegistry', async (req, res) => { method: 'GET', url: API_END_POINTS.userRead(sbUserId), }) - if (sbUserReadResponse.data.params.status !== 'success') { + if (sbUserReadResponse.data.params.status !== 'SUCCESS') { res.status(500).send(failedToReadUser) return } else { @@ -668,7 +668,7 @@ profileDeatailsApi.post('/createUserWithoutInvitationEmail', async (req, res) => method: 'GET', url: API_END_POINTS.kongUserRead(sbUserId), }) - if (sbUserReadResponse.data.params.status !== 'success') { + if (sbUserReadResponse.data.params.status !== 'SUCCESS') { res.status(500).send(failedToReadUser) return } From 67247528f3c48a884568b7377064074cb0eaae9e Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 7 Apr 2022 10:30:27 +0530 Subject: [PATCH 304/856] reverting fix for user create to avoid confusion with versions --- src/protectedApi_v8/user/profile-details.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/protectedApi_v8/user/profile-details.ts b/src/protectedApi_v8/user/profile-details.ts index a4597a82..f1d59d52 100644 --- a/src/protectedApi_v8/user/profile-details.ts +++ b/src/protectedApi_v8/user/profile-details.ts @@ -291,7 +291,7 @@ profileDeatailsApi.post('/createUser', async (req, res) => { method: 'GET', url: API_END_POINTS.kongUserRead(sbUserId), }) - if (sbUserReadResponse.data.params.status !== 'SUCCESS') { + if (sbUserReadResponse.data.params.status !== 'success') { res.status(500).send(failedToReadUser) return } @@ -369,7 +369,7 @@ profileDeatailsApi.post('/createUser', async (req, res) => { }) logInfo('Received response from password reset -> ' + passwordResetResponse) - if (passwordResetResponse.data.params.status === 'SUCCESS') { + if (passwordResetResponse.data.params.status === 'success') { const welcomeMailRequest = { allowedLoging: 'You can use your email to Login', body: 'Hello', @@ -394,7 +394,7 @@ profileDeatailsApi.post('/createUser', async (req, res) => { url: API_END_POINTS.kongSendWelcomeEmail, }) - if (welcomeMailResponse.data.params.status !== 'SUCCESS') { + if (welcomeMailResponse.data.params.status !== 'success') { res.status(500).send('Failed to send Welcome Email.') return } @@ -576,7 +576,7 @@ profileDeatailsApi.post('/createUserV2WithoutRegistry', async (req, res) => { method: 'GET', url: API_END_POINTS.userRead(sbUserId), }) - if (sbUserReadResponse.data.params.status !== 'SUCCESS') { + if (sbUserReadResponse.data.params.status !== 'success') { res.status(500).send(failedToReadUser) return } else { @@ -668,7 +668,7 @@ profileDeatailsApi.post('/createUserWithoutInvitationEmail', async (req, res) => method: 'GET', url: API_END_POINTS.kongUserRead(sbUserId), }) - if (sbUserReadResponse.data.params.status !== 'SUCCESS') { + if (sbUserReadResponse.data.params.status !== 'success') { res.status(500).send(failedToReadUser) return } From 9fcd174ed6bbe5a6b3e9c66e6f95022c935412db Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 7 Apr 2022 10:35:35 +0530 Subject: [PATCH 305/856] fix for user create issue --- src/protectedApi_v8/user/profile-details.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/protectedApi_v8/user/profile-details.ts b/src/protectedApi_v8/user/profile-details.ts index f1d59d52..de1e02e8 100644 --- a/src/protectedApi_v8/user/profile-details.ts +++ b/src/protectedApi_v8/user/profile-details.ts @@ -291,7 +291,7 @@ profileDeatailsApi.post('/createUser', async (req, res) => { method: 'GET', url: API_END_POINTS.kongUserRead(sbUserId), }) - if (sbUserReadResponse.data.params.status !== 'success') { + if (sbUserReadResponse.data.params.status !== 'SUCCESS') { res.status(500).send(failedToReadUser) return } @@ -369,7 +369,7 @@ profileDeatailsApi.post('/createUser', async (req, res) => { }) logInfo('Received response from password reset -> ' + passwordResetResponse) - if (passwordResetResponse.data.params.status === 'success') { + if (passwordResetResponse.data.params.status === 'SUCCESS') { const welcomeMailRequest = { allowedLoging: 'You can use your email to Login', body: 'Hello', @@ -394,7 +394,7 @@ profileDeatailsApi.post('/createUser', async (req, res) => { url: API_END_POINTS.kongSendWelcomeEmail, }) - if (welcomeMailResponse.data.params.status !== 'success') { + if (welcomeMailResponse.data.params.status !== 'SUCCESS') { res.status(500).send('Failed to send Welcome Email.') return } @@ -488,7 +488,7 @@ profileDeatailsApi.post('/createUserV2WithRegistry', async (req, res) => { method: 'GET', url: API_END_POINTS.userRead(sbUserId), }) - if (sbUserReadResponse.data.params.status !== 'success') { + if (sbUserReadResponse.data.params.status !== 'SUCCESS') { res.status(500).send(failedToReadUser) return } @@ -576,7 +576,7 @@ profileDeatailsApi.post('/createUserV2WithoutRegistry', async (req, res) => { method: 'GET', url: API_END_POINTS.userRead(sbUserId), }) - if (sbUserReadResponse.data.params.status !== 'success') { + if (sbUserReadResponse.data.params.status !== 'SUCCESS') { res.status(500).send(failedToReadUser) return } else { @@ -668,7 +668,7 @@ profileDeatailsApi.post('/createUserWithoutInvitationEmail', async (req, res) => method: 'GET', url: API_END_POINTS.kongUserRead(sbUserId), }) - if (sbUserReadResponse.data.params.status !== 'success') { + if (sbUserReadResponse.data.params.status !== 'SUCCESS') { res.status(500).send(failedToReadUser) return } From fb1d2ef344c35d380c418ed20f863d76cdba8ee7 Mon Sep 17 00:00:00 2001 From: Darshan Nagesh Date: Wed, 13 Apr 2022 13:43:45 +0530 Subject: [PATCH 306/856] Update proxyCreator.ts Adding loggers to look into session for Root Org ID --- src/utils/proxyCreator.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 01fba08b..7b9720dc 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -159,6 +159,9 @@ export function proxyCreatorSunbird(route: Router, targetUrl: string, _timeout = // tslint:disable-next-line: no-console console.log('REQ_URL_ORIGINAL proxyCreatorSunbird ======= discussion', url) } + + // tslint:disable-next-line: no-console + console.log('REQ_SESSION proxyCreatorSunbird : ', req.session) if (req.originalUrl.includes('/dashboard') && !req.originalUrl.includes('/dashboard/analytics/getChartV2/Karmayogi') && req.session) { if (req.originalUrl.includes('?')) { From 77cc1f38c872479ece5cc20fb5bdbcd30d3cd840 Mon Sep 17 00:00:00 2001 From: wilkysingh-tarento Date: Thu, 14 Apr 2022 16:32:24 +0530 Subject: [PATCH 307/856] rating whitelist api updated --- src/utils/whitelistApis.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index b9bba53f..77423a25 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1700,7 +1700,7 @@ export const API_LIST = { ], checksNeeded: [CHECK.ROLE], }, - '/proxies/v8/ratings/v1/read/:activity_id/:activity_type/:user_id': { + '/proxies/v8/ratings/v1/read/:activityId/:activityType/:userId': { ROLE_CHECK: [ ROLE.PUBLIC, ], @@ -1712,7 +1712,7 @@ export const API_LIST = { ], checksNeeded: [CHECK.ROLE], }, - '/proxies/v8/ratings/v1/summary/:activity_id/:activity_type': { + '/proxies/v8/ratings/v1/summary/:activityId/:activityType': { ROLE_CHECK: [ ROLE.PUBLIC, ], @@ -1950,9 +1950,9 @@ export const API_LIST = { '/proxies/v8/questionset/v1/retire/:id', '/proxies/v8/questionset/v1/reject/:id', '/proxies/v8/questionset/v1/hierarchy/:id', - '/proxies/v8/ratings/v1/read/:activity_id/:activity_type/:user_id', + '/proxies/v8/ratings/v1/read/:activityId/:activityType/:userId', '/proxies/v8/ratings/v1/upsert', - '/proxies/v8/ratings/v1/summary/:activity_id/:activity_type', + '/proxies/v8/ratings/v1/summary/:activityId/:activityType', '/proxies/v8/ratings/v1/ratingLookUp', ], } From daaf6c44923d7709c03c56f41935570a01ac96b0 Mon Sep 17 00:00:00 2001 From: somvitbhowmik <38094153+somvitbhowmik@users.noreply.github.com> Date: Fri, 22 Apr 2022 23:11:03 +0530 Subject: [PATCH 308/856] fix for AuthToken and http host for certificate --- src/protectedApi_v8/cohorts.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/protectedApi_v8/cohorts.ts b/src/protectedApi_v8/cohorts.ts index 9bff996e..0f7c51e3 100644 --- a/src/protectedApi_v8/cohorts.ts +++ b/src/protectedApi_v8/cohorts.ts @@ -7,16 +7,16 @@ import { ERROR } from '../utils/message' import { extractAuthorizationFromRequest, extractUserIdFromRequest, extractUserToken } from '../utils/requestExtract' const API_END_POINTS = { - addTemplate: `https://igot-dev.in/api/course/batch/cert/v1/template/add`, + addTemplate: `${CONSTANTS.HTTPS_HOST}/api/course/batch/cert/v1/template/add`, autoenrollment: `${CONSTANTS.KONG_API_BASE}/v1/autoenrollment`, batchParticipantsApi: `${CONSTANTS.KONG_API_BASE}/course/v1/batch/participants/list`, cohorts: `${CONSTANTS.KONG_API_BASE}/v2/resources`, - downloadCert: (certId: string) => `https://igot-dev.in/api/certreg/v2/certs/download/${certId}`, + downloadCert: (certId: string) => `${CONSTANTS.HTTPS_HOST}/api/certreg/v2/certs/download/${certId}`, groupCohorts: (groupId: number) => `${CONSTANTS.USER_PROFILE_API_BASE}/groups/${groupId}/users `, hierarchyApiEndPoint: (contentId: string) => `${CONSTANTS.KNOWLEDGE_MW_API_BASE}/action/content/v3/hierarchy/${contentId}?hierarchyType=detail`, - issueCert: 'https://igot-dev.in/api/course/batch/cert/v1/issue?reIssue=true', + issueCert: '${CONSTANTS.HTTPS_HOST}/api/course/batch/cert/v1/issue?reIssue=true', kongSearchUser: `${CONSTANTS.KONG_API_BASE}/user/v1/search`, searchUserRegistry: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/search/profile`, } @@ -174,7 +174,7 @@ cohortsApi.patch('/course/batch/cert/template/add', async (req, res) => { const response = await axios.patch(API_END_POINTS.addTemplate, template, { ...axiosRequestConfig, headers: { - Authorization: CONSTANTS.CERT_AUTH_TOKEN, + Authorization: CONSTANTS.SB_API_KEY, /* tslint:disable-next-line */ 'x-authenticated-user-token': extractUserToken(req), }, @@ -198,7 +198,7 @@ cohortsApi.post('/course/batch/cert/issue', async (req, res) => { const response = await axios.post(API_END_POINTS.issueCert, template, { ...axiosRequestConfig, headers: { - Authorization: CONSTANTS.CERT_AUTH_TOKEN, + Authorization: CONSTANTS.SB_API_KEY, /* tslint:disable-next-line */ 'x-authenticated-user-token': extractUserToken(req), }, @@ -222,7 +222,7 @@ cohortsApi.get('/course/batch/cert/download/:certId', async (req, res) => { const response = await axios.get(API_END_POINTS.downloadCert(certId), { ...axiosRequestConfig, headers: { - Authorization: CONSTANTS.CERT_AUTH_TOKEN, + Authorization: CONSTANTS.SB_API_KEY, /* tslint:disable-next-line */ 'x-authenticated-user-token': extractUserToken(req), }, From 8695219e749782b60a0280d7212856454e48cf45 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Fri, 6 May 2022 16:16:20 +0530 Subject: [PATCH 309/856] Verifying error for reset call --- src/utils/apiWhiteList.ts | 2 +- src/utils/index.html | 92 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 src/utils/index.html diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index 6e3c0582..d7e17d9b 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -243,7 +243,7 @@ const respond403 = (req: Request, res: Response) => { const respond419 = (req: Request, res: Response) => { const REQ_URL = req.path if (_.includes(REQ_URL, '/reset')) { - res.sendFile('../../index.html') + res.sendFile('index.html') } else { const err = ({ msg: 'API WHITELIST :: Unauthorized access for API [ ' + REQ_URL + ' ]', url: REQ_URL }) logError(err.msg) diff --git a/src/utils/index.html b/src/utils/index.html new file mode 100644 index 00000000..afc11a8d --- /dev/null +++ b/src/utils/index.html @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + +
    +
    +
    + logo +
    +
    + slider1 +
    +
    +
    +
    +
    + logo +
    +
    +

    You are logged out!

    +
    +
    + + +
    +
    + +
    +
    + + \ No newline at end of file From 43ca2b06ed4afb3cb7709fdd73d9b6be58cec933 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Fri, 6 May 2022 16:27:59 +0530 Subject: [PATCH 310/856] Fixed path issue and added v5 user read --- src/proxies_v8/proxies_v8.ts | 4 ++++ src/utils/apiWhiteList.ts | 2 +- src/utils/whitelistApis.ts | 16 ++++++++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index bdb76c8a..1c5c0fb4 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -194,6 +194,10 @@ proxiesV8.use('/api/user/v2/read', proxyCreatorToAppentUserId(express.Router(), `${CONSTANTS.KONG_API_BASE}/user/v2/read/`) ) +proxiesV8.use('/api/user/v5/read', + proxyCreatorToAppentUserId(express.Router(), `${CONSTANTS.KONG_API_BASE}/user/v5/read/`) +) + proxiesV8.use([ '/action/questionset/v1/*', '/action/question/v1/*', diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index d7e17d9b..46d8388c 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -243,7 +243,7 @@ const respond403 = (req: Request, res: Response) => { const respond419 = (req: Request, res: Response) => { const REQ_URL = req.path if (_.includes(REQ_URL, '/reset')) { - res.sendFile('index.html') + res.sendFile(__dirname + 'index.html') } else { const err = ({ msg: 'API WHITELIST :: Unauthorized access for API [ ' + REQ_URL + ' ]', url: REQ_URL }) logError(err.msg) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 77423a25..cbf5c04e 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -69,6 +69,20 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/api/user/v5/read': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/api/user/v5/read/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/event/v4/read/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1735,6 +1749,8 @@ export const API_LIST = { '/authApi/readCert/:certId', '/proxies/v8/api/user/v2/read', '/proxies/v8/api/user/v2/read/:id', + '/proxies/v8/api/user/v5/read', + '/proxies/v8/api/user/v5/read/:id', '/proxies/v8/event/v4/read/:do_id', '/proxies/v8/event/v4/publish/:do_id', '/proxies/v8/event/v4/create', From af2f6a83598769984b87141381b5952ef1432bfd Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Fri, 6 May 2022 16:33:38 +0530 Subject: [PATCH 311/856] Index file path fix --- src/utils/apiWhiteList.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index 46d8388c..2dfd0f80 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -243,7 +243,7 @@ const respond403 = (req: Request, res: Response) => { const respond419 = (req: Request, res: Response) => { const REQ_URL = req.path if (_.includes(REQ_URL, '/reset')) { - res.sendFile(__dirname + 'index.html') + res.sendFile(__dirname + '/index.html') } else { const err = ({ msg: 'API WHITELIST :: Unauthorized access for API [ ' + REQ_URL + ' ]', url: REQ_URL }) logError(err.msg) From 14a9a4eaed9af1b5d3aea9700f31676838470298 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Fri, 13 May 2022 15:20:26 +0530 Subject: [PATCH 312/856] Fix for create user issue --- src/protectedApi_v8/user/profile-details.ts | 29 ++++++++++++--------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/protectedApi_v8/user/profile-details.ts b/src/protectedApi_v8/user/profile-details.ts index de1e02e8..59a1fad3 100644 --- a/src/protectedApi_v8/user/profile-details.ts +++ b/src/protectedApi_v8/user/profile-details.ts @@ -216,7 +216,7 @@ const createUserFailed = 'ERROR CREATING USER >' const failedToUpdateUser = 'Failed to update user profile data.' const unknownError = 'Failed due to unknown reason' -// tslint:disable-next-line: max-lines-per-function +// tslint:disable-next-line: all profileDeatailsApi.post('/createUser', async (req, res) => { try { const sbChannel = req.body.personalDetails.channel @@ -224,6 +224,7 @@ profileDeatailsApi.post('/createUser', async (req, res) => { res.status(400).send(channelParamMissing) return } + let statusString = '' const sbemail_ = req.body.personalDetails.email const sbemailVerified_ = true const sbfirstName_ = req.body.personalDetails.firstName @@ -291,7 +292,8 @@ profileDeatailsApi.post('/createUser', async (req, res) => { method: 'GET', url: API_END_POINTS.kongUserRead(sbUserId), }) - if (sbUserReadResponse.data.params.status !== 'SUCCESS') { + statusString = sbUserReadResponse.data.params.status + if (statusString.toUpperCase() !== 'SUCCESS') { res.status(500).send(failedToReadUser) return } @@ -368,8 +370,8 @@ profileDeatailsApi.post('/createUser', async (req, res) => { url: API_END_POINTS.kongUserResetPassword, }) logInfo('Received response from password reset -> ' + passwordResetResponse) - - if (passwordResetResponse.data.params.status === 'SUCCESS') { + statusString = passwordResetResponse.data.params.status + if (statusString.toUpperCase() === 'SUCCESS') { const welcomeMailRequest = { allowedLoging: 'You can use your email to Login', body: 'Hello', @@ -393,8 +395,8 @@ profileDeatailsApi.post('/createUser', async (req, res) => { method: 'POST', url: API_END_POINTS.kongSendWelcomeEmail, }) - - if (welcomeMailResponse.data.params.status !== 'SUCCESS') { + statusString = welcomeMailResponse.data.params.status + if (statusString.toUpperCase() !== 'SUCCESS') { res.status(500).send('Failed to send Welcome Email.') return } @@ -448,7 +450,7 @@ profileDeatailsApi.post('/createUserV2WithRegistry', async (req, res) => { const sbemailVerified_ = true const sbfirstName_ = req.body.personalDetails.firstName const sblastName_ = req.body.personalDetails.lastName - + let statusString = '' const searchresponse = await axios({ ...axiosRequestConfig, data: { request: { query: '', filters: { email: sbemail_.toLowerCase() } } }, @@ -488,7 +490,8 @@ profileDeatailsApi.post('/createUserV2WithRegistry', async (req, res) => { method: 'GET', url: API_END_POINTS.userRead(sbUserId), }) - if (sbUserReadResponse.data.params.status !== 'SUCCESS') { + statusString = sbUserReadResponse.data.params.status + if (statusString.toUpperCase() !== 'SUCCESS') { res.status(500).send(failedToReadUser) return } @@ -536,7 +539,7 @@ profileDeatailsApi.post('/createUserV2WithoutRegistry', async (req, res) => { const sbemailVerified_ = true const sbfirstName_ = req.body.personalDetails.firstName const sblastName_ = req.body.personalDetails.lastName - + let statusString = '' const searchresponse = await axios({ ...axiosRequestConfig, data: { request: { query: '', filters: { email: sbemail_.toLowerCase() } } }, @@ -576,7 +579,8 @@ profileDeatailsApi.post('/createUserV2WithoutRegistry', async (req, res) => { method: 'GET', url: API_END_POINTS.userRead(sbUserId), }) - if (sbUserReadResponse.data.params.status !== 'SUCCESS') { + statusString = sbUserReadResponse.data.params.status + if (statusString.toUpperCase() !== 'SUCCESS') { res.status(500).send(failedToReadUser) return } else { @@ -606,7 +610,7 @@ profileDeatailsApi.post('/createUserWithoutInvitationEmail', async (req, res) => const sbemailVerified_ = true const sbfirstName_ = req.body.personalDetails.firstName const sblastName_ = req.body.personalDetails.lastName - + let statusString = '' const searchresponse = await axios({ ...axiosRequestConfig, data: { request: { query: '', filters: { email: sbemail_.toLowerCase() } } }, @@ -668,7 +672,8 @@ profileDeatailsApi.post('/createUserWithoutInvitationEmail', async (req, res) => method: 'GET', url: API_END_POINTS.kongUserRead(sbUserId), }) - if (sbUserReadResponse.data.params.status !== 'SUCCESS') { + statusString = sbUserReadResponse.data.params.status + if (statusString.toUpperCase() !== 'SUCCESS') { res.status(500).send(failedToReadUser) return } From 623ae10e60d4b463678a24a9e8e36db5b9613221 Mon Sep 17 00:00:00 2001 From: juhi agarwal Date: Mon, 16 May 2022 13:45:02 +0530 Subject: [PATCH 313/856] Update whitelistApis.ts Added the urls for the user's last login update to the whitelisted urls --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index cbf5c04e..4730baa3 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -76,6 +76,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/user/v1/updateLogin': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/api/user/v5/read/:id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1751,6 +1758,7 @@ export const API_LIST = { '/proxies/v8/api/user/v2/read/:id', '/proxies/v8/api/user/v5/read', '/proxies/v8/api/user/v5/read/:id', + '/proxies/v8/user/v1/updateLogin', '/proxies/v8/event/v4/read/:do_id', '/proxies/v8/event/v4/publish/:do_id', '/proxies/v8/event/v4/create', From 22a3480ca64d1dea118b6684f20d0bb816ab89aa Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Tue, 17 May 2022 14:22:54 +0530 Subject: [PATCH 314/856] Added role check for Reset API --- src/utils/whitelistApis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 4730baa3..bc353139 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1304,7 +1304,7 @@ export const API_LIST = { ], }, '/reset': { - checksNeeded: [], + checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, From e60ed65f43743bc2f934a107c41116056297cb3d Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Wed, 18 May 2022 15:13:27 +0530 Subject: [PATCH 315/856] Clearing cookies for logout --- src/utils/apiWhiteList.ts | 6 ++++++ src/utils/custom-keycloak.ts | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index 2dfd0f80..45c42a36 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -86,8 +86,10 @@ const urlChecks = { const data = (roleData) ? roleData : [] logInfo('Portal_API_WHITELIST : Middleware for URL [ ' + REQ_URL + ' ]') if (_.includes(rolesForURL, 'ALL') && data.length > 0) { + logInfo('RolesForAll is getting called') resolve() } else if (_.intersection(rolesForURL, data).length > 0) { + logInfo('RolesForUrl is getting called') resolve() } else { return reject('User doesn\'t have appropriate roles') @@ -190,6 +192,10 @@ const executeChecks = async (req: Request, res: Response , next: NextFunction, c if (_isRejected) { throw new Error(_isRejected.reason) } else { + if (_.includes(req.path, '/reset')) { + logInfo('Trying to reset cookies') + res.clearCookie('connect.sid', { path: '/' }); + } next() } } else { diff --git a/src/utils/custom-keycloak.ts b/src/utils/custom-keycloak.ts index 6b073ce5..571006df 100644 --- a/src/utils/custom-keycloak.ts +++ b/src/utils/custom-keycloak.ts @@ -84,7 +84,8 @@ export class CustomKeycloak { // tslint:disable-next-line: no-any deauthenticated = (request: any) => { delete request.session.userRoles - delete request.session.userId + delete request.session.userId + request.session.destroy(); logInfo(`${process.pid}: User Deauthenticated`) } From f287d74244ba28f11355629d04506f3096f5e63b Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Wed, 18 May 2022 15:41:06 +0530 Subject: [PATCH 316/856] New page for logout --- src/server.ts | 5 +++-- src/utils/apiWhiteList.ts | 4 ---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/server.ts b/src/server.ts index 705e8ba9..6d88142c 100644 --- a/src/server.ts +++ b/src/server.ts @@ -165,8 +165,9 @@ export class Server { private resetCookies() { this.app.use('/reset', (_req, res) => { logInfo('CLEARING RES COOKIES') - res.cookie('connect.sid', '', { expires: new Date() }) - res.redirect('/reset') + res.clearCookie('connect.sid', { path: '/' }) + //res.cookie('connect.sid', '', { expires: new Date() }) + res.redirect('/public/logout') }) } } diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index 45c42a36..08072c40 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -192,10 +192,6 @@ const executeChecks = async (req: Request, res: Response , next: NextFunction, c if (_isRejected) { throw new Error(_isRejected.reason) } else { - if (_.includes(req.path, '/reset')) { - logInfo('Trying to reset cookies') - res.clearCookie('connect.sid', { path: '/' }); - } next() } } else { From ffe4f1e41caed5b146fb3e8870a7add224942c2e Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Wed, 18 May 2022 19:58:27 +0530 Subject: [PATCH 317/856] Added more logs --- src/utils/custom-keycloak.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/utils/custom-keycloak.ts b/src/utils/custom-keycloak.ts index 571006df..261047e8 100644 --- a/src/utils/custom-keycloak.ts +++ b/src/utils/custom-keycloak.ts @@ -83,13 +83,15 @@ export class CustomKeycloak { // tslint:disable-next-line: no-any deauthenticated = (request: any) => { + logInfo('Initiating Deauthentication process. current SessionId' + request.session.id + ', value: ' + request.session) delete request.session.userRoles - delete request.session.userId - request.session.destroy(); + delete request.session.userId + request.session.destroy() logInfo(`${process.pid}: User Deauthenticated`) } protect = (req: express.Request, res: express.Response, next: express.NextFunction) => { + logInfo('KeyCloak.Protect :: ' + req.path) const keycloak = this.getKeyCloakObject(req) return keycloak.protect()(req, res, next) } From 3def0f133233f3dc2844dfd269e63158f0894220 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Wed, 18 May 2022 21:25:41 +0530 Subject: [PATCH 318/856] Added more logs --- src/utils/custom-keycloak.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/custom-keycloak.ts b/src/utils/custom-keycloak.ts index 261047e8..c2e453b3 100644 --- a/src/utils/custom-keycloak.ts +++ b/src/utils/custom-keycloak.ts @@ -83,7 +83,7 @@ export class CustomKeycloak { // tslint:disable-next-line: no-any deauthenticated = (request: any) => { - logInfo('Initiating Deauthentication process. current SessionId' + request.session.id + ', value: ' + request.session) + logInfo('Initiating Deauthentication process. current SessionId' + request.session.id + ', value: ' + JSON.stringify(request.session)) delete request.session.userRoles delete request.session.userId request.session.destroy() From 9c4e27b7063cc1fa1831f510a970e7029b96ef2a Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 19 May 2022 13:29:53 +0530 Subject: [PATCH 319/856] Fix for logout API. Implemented keycloak logout API call. --- src/server.ts | 4 +-- src/utils/apiWhiteList.ts | 1 + src/utils/custom-keycloak.ts | 51 ++++++++++++++++++++++++++++-------- src/utils/whitelistApis.ts | 12 ++++----- 4 files changed, 49 insertions(+), 19 deletions(-) diff --git a/src/server.ts b/src/server.ts index 6d88142c..9ac5c724 100644 --- a/src/server.ts +++ b/src/server.ts @@ -108,7 +108,7 @@ export class Server { ) // TODO: See what needs to be logged this.app.use((req, _, next) => { - logInfo(`Worker ${process.pid} : ${req.url}`) + logInfo(`Server:ConfigureMiddleWare:: Worker ${process.pid} : ${req.url}`) next() }) this.app.use(morgan('dev')) @@ -166,7 +166,7 @@ export class Server { this.app.use('/reset', (_req, res) => { logInfo('CLEARING RES COOKIES') res.clearCookie('connect.sid', { path: '/' }) - //res.cookie('connect.sid', '', { expires: new Date() }) + // res.cookie('connect.sid', '', { expires: new Date() }) res.redirect('/public/logout') }) } diff --git a/src/utils/apiWhiteList.ts b/src/utils/apiWhiteList.ts index 08072c40..f4299707 100644 --- a/src/utils/apiWhiteList.ts +++ b/src/utils/apiWhiteList.ts @@ -85,6 +85,7 @@ const urlChecks = { const roleData = _.get(req, 'session.userRoles') const data = (roleData) ? roleData : [] logInfo('Portal_API_WHITELIST : Middleware for URL [ ' + REQ_URL + ' ]') + logInfo('Configured Roles for URL -> ' + JSON.stringify(rolesForURL)) if (_.includes(rolesForURL, 'ALL') && data.length > 0) { logInfo('RolesForAll is getting called') resolve() diff --git a/src/utils/custom-keycloak.ts b/src/utils/custom-keycloak.ts index c2e453b3..610d5e34 100644 --- a/src/utils/custom-keycloak.ts +++ b/src/utils/custom-keycloak.ts @@ -1,6 +1,7 @@ import * as express from 'express' import expressSession from 'express-session' import keycloakConnect from 'keycloak-connect' +import request from 'request' import { getKeycloakConfig } from '../configs/keycloak.config' import { CONSTANTS } from './env' import { logError, logInfo } from './logger' @@ -54,19 +55,19 @@ export class CustomKeycloak { } // tslint:disable-next-line: no-any - authenticated = (request: any, next: any) => { + authenticated = (reqObj: any, next: any) => { logInfo('Step 3: authenticated function', '------', new Date().toString()) try { - const userId = request.kauth.grant.access_token.content.sub.split(':') - request.session.userId = userId[userId.length - 1] + const userId = reqObj.kauth.grant.access_token.content.sub.split(':') + reqObj.session.userId = userId[userId.length - 1] logInfo('userId ::', userId, '------', new Date().toString()) } catch (err) { - logError('userId conversation error' + request.kauth.grant.access_token.content.sub, '------', new Date().toString()) + logError('userId conversation error' + reqObj.kauth.grant.access_token.content.sub, '------', new Date().toString()) } const postLoginRequest = [] // tslint:disable-next-line: no-any postLoginRequest.push((callback: any) => { - PERMISSION_HELPER.getCurrentUserRoles(request, callback) + PERMISSION_HELPER.getCurrentUserRoles(reqObj, callback) }) // tslint:disable-next-line: no-any @@ -82,16 +83,44 @@ export class CustomKeycloak { } // tslint:disable-next-line: no-any - deauthenticated = (request: any) => { - logInfo('Initiating Deauthentication process. current SessionId' + request.session.id + ', value: ' + JSON.stringify(request.session)) - delete request.session.userRoles - delete request.session.userId - request.session.destroy() + deauthenticated = (reqObj: any) => { + const keyCloakPropertyName = 'keycloak-token' + if (reqObj.session.hasOwnProperty(keyCloakPropertyName)) { + const keycloakToken = reqObj.session[keyCloakPropertyName] + if (keycloakToken) { + const tokenObject = JSON.parse(keycloakToken) + const refreshToken = tokenObject.refresh_token + if (refreshToken) { + const host = reqObj.get('host') + const urlValue = `https://${host}` + '/auth/realms/' + CONSTANTS.KEYCLOAK_REALM + '/protocol/openid-connect/logout' + try { + request.post({ + form: { + client_id: 'portal', + refresh_token: refreshToken, + }, + url: urlValue, + }) + } catch (err) { + // tslint:disable-next-line: no-console + console.log('Failed to call keycloak logout API ', err, '------', new Date().toString()) + } + } else { + logError('Not able to retrieve refresh_token value from Session. Logout process failed.') + } + } else { + logError('Not able to retrieve keycloak-token value from Session. Logout process failed.') + } + } else { + logError('Session does not have property with name: ' + keyCloakPropertyName) + } + delete reqObj.session.userRoles + delete reqObj.session.userId + reqObj.session.destroy() logInfo(`${process.pid}: User Deauthenticated`) } protect = (req: express.Request, res: express.Response, next: express.NextFunction) => { - logInfo('KeyCloak.Protect :: ' + req.path) const keycloak = this.getKeyCloakObject(req) return keycloak.protect()(req, res, next) } diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index bc353139..16b7f278 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -59,35 +59,35 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.PUBLIC, 'ALL', ], }, '/proxies/v8/api/user/v2/read/:id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.PUBLIC, 'ALL', ], }, '/proxies/v8/api/user/v5/read': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.PUBLIC, 'ALL', ], }, '/proxies/v8/user/v1/updateLogin': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.PUBLIC, 'ALL', ], }, '/proxies/v8/api/user/v5/read/:id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.PUBLIC, 'ALL', ], }, '/proxies/v8/event/v4/read/:do_id': { @@ -1307,7 +1307,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.PUBLIC, 'ALL' ], }, '/protected/v8/user/evaluate/assessment/submit/v2': { From cd288abac9fd9fd69b92ed01600dd1f952e30666 Mon Sep 17 00:00:00 2001 From: sreeragksgh <58926794+sreeragksgh@users.noreply.github.com> Date: Sat, 21 May 2022 17:16:25 +0530 Subject: [PATCH 320/856] userWFApplicationFieldsSearch API has been provided Public Role access --- src/utils/whitelistApis.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 16b7f278..ffc8bd2f 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1174,10 +1174,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.WAT_MEMBER, - ROLE.CBC_ADMIN, - ROLE.SPV_ADMIN, + ROLE.PUBLIC, ], }, '/protected/v8/user/details/detailV1': { From 6a5cda4288d94043bc6dd483f2b31be920642c25 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Wed, 25 May 2022 10:50:15 +0530 Subject: [PATCH 321/856] Added publisher role to hierarchyUpdate API --- src/utils/whitelistApis.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index ffc8bd2f..71316f82 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -254,6 +254,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ + ROLE.CONTENT_PUBLISHER, ROLE.CONTENT_REVIEWER, ], }, From 17d1f6f1f46a7efd4c80ee1c44c0f2852613a88d Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Thu, 26 May 2022 16:35:13 +0530 Subject: [PATCH 322/856] Updated the response structure for notifyContentState --- src/proxies_v8/proxies_v8.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 1c5c0fb4..bbd0f7b8 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -401,8 +401,8 @@ proxiesV8.post('/notifyContentState', async (req, res) => { }) logInfo('Response -> ' + JSON.stringify(stateEmailResponse.data)) if (!stateEmailResponse.data.result.response) { - res.status(400).send('Failed to send content state notification...') + res.status(400).send(stateEmailResponse.data) } else { - res.status(200).send('Email sent successfully.') + res.status(200).send(stateEmailResponse.data) } }) From 24e044a72a908e400fbcfcf743166c9ea6e67d24 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Mon, 30 May 2022 12:55:51 +0530 Subject: [PATCH 323/856] Nationality updated to "Indian" instead of "India" --- src/static-data/nationality.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/static-data/nationality.json b/src/static-data/nationality.json index a13ef62b..d4c73c49 100644 --- a/src/static-data/nationality.json +++ b/src/static-data/nationality.json @@ -1,7 +1,7 @@ { "nationalities": [ { - "name": "India", + "name": "Indian", "countryCode": "+91" }, { @@ -785,4 +785,4 @@ "countryCode": "+263" } ] -} \ No newline at end of file +} From 5fb447e77452e936ca53d7ba25457fb6c5d2294d Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Mon, 6 Jun 2022 19:48:56 +0530 Subject: [PATCH 324/856] Using public/home for logout --- src/server.ts | 9 +++++++-- src/utils/env.ts | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/server.ts b/src/server.ts index 9ac5c724..76c634e5 100644 --- a/src/server.ts +++ b/src/server.ts @@ -166,8 +166,13 @@ export class Server { this.app.use('/reset', (_req, res) => { logInfo('CLEARING RES COOKIES') res.clearCookie('connect.sid', { path: '/' }) - // res.cookie('connect.sid', '', { expires: new Date() }) - res.redirect('/public/logout') + const host = _req.get('host') + let redirectUrl = '/public/logout' + logInfo('Reset Cookies... received host value ' + host) + if (host === `${CONSTANTS.KARMAYOGI_PORTAL_HOST}`) { + redirectUrl = '/public/home' + } + res.redirect(redirectUrl) }) } } diff --git a/src/utils/env.ts b/src/utils/env.ts index 08ae7095..1d86cce0 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -159,6 +159,7 @@ export const CONSTANTS = { CERT_AUTH_TOKEN: 'bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJRekw4VVA1dUtqUFdaZVpMd1ZtTFJvNHdqWTg2a2FrcSJ9.TPjV0xLacSbp3FbJ7XeqHoKFN35Rl4YHx3DZNN9pm0o', // tslint:disable-next-line: max-line-length KEYCLOAK_PUBLIC_KEY: 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoBa+pmsxpPhGED2/zexM/PL+rLv2YnLCbX2agY2fllucCvnPNmWRTyg8L+qgkJBpHuhcHqXEFTfBm2WCe4yke+0LnGi6SGsCJTrc2Sh+Lgo/L9r1lr7uOoD8BHE6ywnbHG2ok7qyomCTD9LpG1WXFk4T3WUH9+gN2vm77s3YJz2yfo2UlMd1Dp9nWZhfwB1vwJLuRHnqEKkSH8M2K8SFRdpgOyLdM9fprr7Ebgr7SWdoAy2q3mUONSmDsftKDeEWjr6MXGTATfbMfKE3GFqddMjFHkmvh42aM+RlU3N2xo2msF8M2sYF6YlIDkED+9h+y0QROJjQlBr774GAd06I0wIDAQAB', + KARMAYOGI_PORTAL_HOST: env.KARMAYOGI_PORTAL_HOST || 'igot-dev.in', } export const RESTRICTED_PYTHON_STMT: string[] = process.env.RESTRICTED_CHARACTERS From 49adb09591907ea18593960b4384a6da41c6554b Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Fri, 10 Jun 2022 15:24:38 +0530 Subject: [PATCH 325/856] Added user positions list API --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 71316f82..0c8600b8 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1743,6 +1743,13 @@ export const API_LIST = { ], checksNeeded: [CHECK.ROLE], }, + '/proxies/v8/user/v1/positions': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -1976,5 +1983,6 @@ export const API_LIST = { '/proxies/v8/ratings/v1/upsert', '/proxies/v8/ratings/v1/summary/:activityId/:activityType', '/proxies/v8/ratings/v1/ratingLookUp', + '/proxies/v8/user/v1/positions', ], } From 5f87486e5950fb1cadc3cbc5822ea34b22c443ac Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 16 Jun 2022 17:49:45 +0530 Subject: [PATCH 326/856] Added temp API for org profile read --- src/proxies_v8/proxies_v8.ts | 29 +++++++++++++++++++++++++++++ src/utils/roles.ts | 1 + src/utils/whitelistApis.ts | 12 +++++++++++- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index bbd0f7b8..41688b4f 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -406,3 +406,32 @@ proxiesV8.post('/notifyContentState', async (req, res) => { res.status(200).send(stateEmailResponse.data) } }) + +proxiesV8.get('/org/v1/profile/read/:orgId', async (req, res) => { + const orgId = req.params.orgId + const response = { + id: 'api.org.profile.read', + params: { + status: 'SUCCESS', + }, + responseCode: 'OK', + result: { + response: { + organisationId: orgId, + profile: { + consultancy: {}, + faculty: {}, + infrastructure: {}, + instituteProfile: {}, + platformWalkthrough: {}, + research: {}, + rolesAndFunctions: {}, + trainingPrograms: {}, + }, + }, + }, + + ver: 'v1', + } + res.status(200).send(response) +}) diff --git a/src/utils/roles.ts b/src/utils/roles.ts index 97c93e50..f6a56a05 100644 --- a/src/utils/roles.ts +++ b/src/utils/roles.ts @@ -16,5 +16,6 @@ export const ROLE = { MDO_ADMIN: 'MDO_ADMIN', PUBLIC: 'PUBLIC', SPV_ADMIN: 'SPV_ADMIN', + STATE_ADMIN: 'STATE_ADMIN', WAT_MEMBER: 'WAT_MEMBER', } diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 0c8600b8..c198e998 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -397,6 +397,16 @@ export const API_LIST = { ROLE.SPV_ADMIN, ], }, + '/proxies/v8/org/v1/profile/read/:orgId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.CBC_ADMIN, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, + ROLE.STATE_ADMIN, + ], + }, '/proxies/v8/org/v1/update': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1305,7 +1315,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, 'ALL' + ROLE.PUBLIC, 'ALL', ], }, '/protected/v8/user/evaluate/assessment/submit/v2': { From 82e69321d49accc5c73da3f6ca5b5c1b06578a63 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 16 Jun 2022 18:01:33 +0530 Subject: [PATCH 327/856] Added org profile read in whitelist --- src/utils/whitelistApis.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index c198e998..257fb158 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1818,6 +1818,7 @@ export const API_LIST = { '/proxies/v8/user/private/v1/assign/role', '/proxies/v8/user/v1/search', '/proxies/v8/org/v1/read', + '/proxies/v8/org/v1/profile/read/:orgId', '/proxies/v8/org/v1/create', '/proxies/v8/discussion/tags', '/proxies/v8/user/v1/block', From 7afba4d11ec6b96baf35d1c861bb96bbdf444c70 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 16 Jun 2022 18:09:17 +0530 Subject: [PATCH 328/856] Restructured org profile read API --- src/proxies_v8/proxies_v8.ts | 58 ++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 41688b4f..aeaf2443 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -225,6 +225,35 @@ proxiesV8.use('/notification/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) +proxiesV8.get('/org/v1/profile/read/:orgId', async (req, res) => { + const orgId = req.params.orgId + const response = { + id: 'api.org.profile.read', + params: { + status: 'SUCCESS', + }, + responseCode: 'OK', + result: { + response: { + organisationId: orgId, + profile: { + consultancy: {}, + faculty: {}, + infrastructure: {}, + instituteProfile: {}, + platformWalkthrough: {}, + research: {}, + rolesAndFunctions: {}, + trainingPrograms: {}, + }, + }, + }, + + ver: 'v1', + } + res.status(200).send(response) +}) + proxiesV8.use('/org/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) @@ -406,32 +435,3 @@ proxiesV8.post('/notifyContentState', async (req, res) => { res.status(200).send(stateEmailResponse.data) } }) - -proxiesV8.get('/org/v1/profile/read/:orgId', async (req, res) => { - const orgId = req.params.orgId - const response = { - id: 'api.org.profile.read', - params: { - status: 'SUCCESS', - }, - responseCode: 'OK', - result: { - response: { - organisationId: orgId, - profile: { - consultancy: {}, - faculty: {}, - infrastructure: {}, - instituteProfile: {}, - platformWalkthrough: {}, - research: {}, - rolesAndFunctions: {}, - trainingPrograms: {}, - }, - }, - }, - - ver: 'v1', - } - res.status(200).send(response) -}) From 6e8e837135bb7e4cae023e7f6aef7b0032f2bb2a Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Fri, 17 Jun 2022 12:30:39 +0530 Subject: [PATCH 329/856] Added STATE_ADMIN role few of the APIs --- src/utils/whitelistApis.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 257fb158..20a27426 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -362,6 +362,7 @@ export const API_LIST = { ROLE.CBC_ADMIN, ROLE.MDO_ADMIN, ROLE.SPV_ADMIN, + ROLE.STATE_ADMIN, ], }, '/proxies/v8/org/v1/search': { @@ -378,6 +379,7 @@ export const API_LIST = { ROLE.MDO_ADMIN, ROLE.SPV_ADMIN, ROLE.CBP_ADMIN, + ROLE.STATE_ADMIN, ROLE.CONTENT_CREATOR, ], }, @@ -395,6 +397,7 @@ export const API_LIST = { ROLE.CBC_ADMIN, ROLE.MDO_ADMIN, ROLE.SPV_ADMIN, + ROLE.STATE_ADMIN, ], }, '/proxies/v8/org/v1/profile/read/:orgId': { @@ -414,6 +417,7 @@ export const API_LIST = { ROLE.CBC_ADMIN, ROLE.MDO_ADMIN, ROLE.SPV_ADMIN, + ROLE.STATE_ADMIN, ], }, '/proxies/v8/user/v1/block': { @@ -423,6 +427,7 @@ export const API_LIST = { ROLE.MDO_ADMIN, ROLE.SPV_ADMIN, ROLE.CBP_ADMIN, + ROLE.STATE_ADMIN, ROLE.CONTENT_CREATOR, ], }, @@ -433,6 +438,7 @@ export const API_LIST = { ROLE.MDO_ADMIN, ROLE.SPV_ADMIN, ROLE.CBP_ADMIN, + ROLE.STATE_ADMIN, ROLE.CONTENT_CREATOR, ], }, @@ -443,6 +449,7 @@ export const API_LIST = { ROLE.CBC_ADMIN, ROLE.MDO_ADMIN, ROLE.SPV_ADMIN, + ROLE.STATE_ADMIN, ], }, '/proxies/v8/discussion/tags': { @@ -459,6 +466,7 @@ export const API_LIST = { ROLE.CBC_ADMIN, ROLE.MDO_ADMIN, ROLE.SPV_ADMIN, + ROLE.STATE_ADMIN, ], }, '/proxies/v8/learnervm/private/content/v3/publish/:do_id': { @@ -545,6 +553,7 @@ export const API_LIST = { ROLE.CBC_ADMIN, ROLE.MDO_ADMIN, ROLE.SPV_ADMIN, + ROLE.STATE_ADMIN, ], }, '/proxies/v8/user/private/v1/migrate': { @@ -799,6 +808,7 @@ export const API_LIST = { ROLE.WAT_MEMBER, ROLE.CBC_ADMIN, ROLE.SPV_ADMIN, + ROLE.STATE_ADMIN, ], }, '/protected/v8/user/autocomplete/:query': { @@ -830,6 +840,7 @@ export const API_LIST = { ROLE.WAT_MEMBER, ROLE.CBC_ADMIN, ROLE.SPV_ADMIN, + ROLE.STATE_ADMIN ], }, '/protected/v8/workallocation/getWorkOrders': { From 0c1fac08d3be543cdff833cbf8ff92b34724db01 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Fri, 17 Jun 2022 19:54:29 +0530 Subject: [PATCH 330/856] Added API to list org type --- src/proxies_v8/proxies_v8.ts | 135 +++++++++++++++++++++++++++++++++++ src/utils/whitelistApis.ts | 8 +++ 2 files changed, 143 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index aeaf2443..813f7d0d 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -254,6 +254,141 @@ proxiesV8.get('/org/v1/profile/read/:orgId', async (req, res) => { res.status(200).send(response) }) +proxiesV8.get('/org/v1/list/:type', async (req, res) => { + const type = req.params.type + const content = 'content' + const count = 'count' + const spvList = [ + { + "mapId": "123881", + "orgCode": "M1", + "orgName": "Ministry of Agriculture and Farmers Welfare", + "parentMapId": "SPV", + "sbOrgId": "", + "sbOrgSubType": "", + "sbOrgType": "" + }, + { + "mapId": "123882", + "orgCode": "M2", + "orgName": "Ministry of AYUSH", + "parentMapId": "SPV", + "sbOrgId": "", + "sbOrgSubType": "", + "sbOrgType": "" + } + ]; + const minList123881 = [ + { + "mapId": "124023", + "orgCode": "D1", + "orgName": "Department of Agricultural Research and Education (DARE)", + "parentMapId": "123881", + "sbOrgId": "", + "sbOrgSubType": "", + "sbOrgType": "" + }, + { + "mapId": "124024", + "orgCode": "D2", + "orgName": "Department of Agriculture Cooperation and Farmers Welfare", + "parentMapId": "123881", + "sbOrgId": "", + "sbOrgSubType": "", + "sbOrgType": "" + } + ]; + const minList123882 = [ + { + "mapId": "124026", + "orgCode": "", + "orgName": "NA", + "parentMapId": "123882", + "sbOrgId": "", + "sbOrgSubType": "", + "sbOrgType": "" + } + ]; + const orgList124023 = [ + { + "mapId": "127443", + "orgCode": "O1", + "orgName": "Agricultural Technology Information Centre (ATIC)", + "parentMapId": "124023", + "sbOrgId": "", + "sbOrgSubType": "", + "sbOrgType": "" + }, + { + "mapId": "127444", + "orgCode": "O2", + "orgName": "Agrinnovate India", + "parentMapId": "124023", + "sbOrgId": "", + "sbOrgSubType": "", + "sbOrgType": "" + } + ] + const orgList124026 = [ + { + "mapId": "127515", + "orgCode": "O73", + "orgName": "Central Council for Research in Yoga & Naturopathy (CCRYN) New Delhi", + "parentMapId": "124026", + "sbOrgId": "", + "sbOrgSubType": "", + "sbOrgType": "" + }, + { + "mapId": "127516", + "orgCode": "O74", + "orgName": "Homoeopathic Pharmacopoeia Laboratory (HPL) Ghaziabad Uttar Pradesh", + "parentMapId": "124026", + "sbOrgId": "", + "sbOrgSubType": "", + "sbOrgType": "" + } + ] + + var response = { + id: 'api.org.profile.read', + params: { + status: 'SUCCESS', + }, + responseCode: 'OK', + result: { + response: { + } + } + } + switch (type) { + case 'SPV': + response.result.response[content] = spvList + response.result.response[count] = spvList.length + break; + case '123881': + response.result.response[content] = minList123881 + response.result.response[count] = minList123881.length + break; + case '123882': + response.result.response[content] = minList123882 + response.result.response[count] = minList123882.length + break; + case '124023': + response.result.response[content] = orgList124023 + response.result.response[count] = orgList124023.length + break; + case '124026': + response.result.response[content] = orgList124026 + response.result.response[count] = orgList124026.length + break; + default: + break; + } + + res.status(200).send(response) +}) + proxiesV8.use('/org/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 20a27426..70d243a5 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1771,6 +1771,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/org/v1/list/:type': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -2006,5 +2013,6 @@ export const API_LIST = { '/proxies/v8/ratings/v1/summary/:activityId/:activityType', '/proxies/v8/ratings/v1/ratingLookUp', '/proxies/v8/user/v1/positions', + '/proxies/v8/org/v1/list/:type', ], } From e81bf67c3f406acafa178a9b1bb785b1185518c7 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Fri, 17 Jun 2022 19:57:43 +0530 Subject: [PATCH 331/856] Fixed lint issues --- src/proxies_v8/proxies_v8.ts | 160 +++++++++++++++++------------------ src/utils/whitelistApis.ts | 2 +- 2 files changed, 81 insertions(+), 81 deletions(-) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 813f7d0d..83fac4b8 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -260,97 +260,97 @@ proxiesV8.get('/org/v1/list/:type', async (req, res) => { const count = 'count' const spvList = [ { - "mapId": "123881", - "orgCode": "M1", - "orgName": "Ministry of Agriculture and Farmers Welfare", - "parentMapId": "SPV", - "sbOrgId": "", - "sbOrgSubType": "", - "sbOrgType": "" + mapId: '123881', + orgCode: 'M1', + orgName: 'Ministry of Agriculture and Farmers Welfare', + parentMapId: 'SPV', + sbOrgId: '', + sbOrgSubType: '', + sbOrgType: '', }, { - "mapId": "123882", - "orgCode": "M2", - "orgName": "Ministry of AYUSH", - "parentMapId": "SPV", - "sbOrgId": "", - "sbOrgSubType": "", - "sbOrgType": "" - } - ]; + mapId: '123882', + orgCode: 'M2', + orgName: 'Ministry of AYUSH', + parentMapId: 'SPV', + sbOrgId: '', + sbOrgSubType: '', + sbOrgType: '', + }, + ] const minList123881 = [ { - "mapId": "124023", - "orgCode": "D1", - "orgName": "Department of Agricultural Research and Education (DARE)", - "parentMapId": "123881", - "sbOrgId": "", - "sbOrgSubType": "", - "sbOrgType": "" + mapId: '124023', + orgCode: 'D1', + orgName: 'Department of Agricultural Research and Education (DARE)', + parentMapId: '123881', + sbOrgId: '', + sbOrgSubType: '', + sbOrgType: '', }, { - "mapId": "124024", - "orgCode": "D2", - "orgName": "Department of Agriculture Cooperation and Farmers Welfare", - "parentMapId": "123881", - "sbOrgId": "", - "sbOrgSubType": "", - "sbOrgType": "" - } - ]; + mapId: '124024', + orgCode: 'D2', + orgName: 'Department of Agriculture Cooperation and Farmers Welfare', + parentMapId: '123881', + sbOrgId: '', + sbOrgSubType: '', + sbOrgType: '', + }, + ] const minList123882 = [ { - "mapId": "124026", - "orgCode": "", - "orgName": "NA", - "parentMapId": "123882", - "sbOrgId": "", - "sbOrgSubType": "", - "sbOrgType": "" - } - ]; + mapId: '124026', + orgCode: '', + orgName: 'NA', + parentMapId: '123882', + sbOrgId: '', + sbOrgSubType: '', + sbOrgType: '', + }, + ] const orgList124023 = [ { - "mapId": "127443", - "orgCode": "O1", - "orgName": "Agricultural Technology Information Centre (ATIC)", - "parentMapId": "124023", - "sbOrgId": "", - "sbOrgSubType": "", - "sbOrgType": "" + mapId: '127443', + orgCode: 'O1', + orgName: 'Agricultural Technology Information Centre (ATIC)', + parentMapId: '124023', + sbOrgId: '', + sbOrgSubType: '', + sbOrgType: '', }, { - "mapId": "127444", - "orgCode": "O2", - "orgName": "Agrinnovate India", - "parentMapId": "124023", - "sbOrgId": "", - "sbOrgSubType": "", - "sbOrgType": "" - } + mapId: '127444', + orgCode: 'O2', + orgName: 'Agrinnovate India', + parentMapId: '124023', + sbOrgId: '', + sbOrgSubType: '', + sbOrgType: '', + }, ] const orgList124026 = [ { - "mapId": "127515", - "orgCode": "O73", - "orgName": "Central Council for Research in Yoga & Naturopathy (CCRYN) New Delhi", - "parentMapId": "124026", - "sbOrgId": "", - "sbOrgSubType": "", - "sbOrgType": "" + mapId: '127515', + orgCode: 'O73', + orgName: 'Central Council for Research in Yoga & Naturopathy (CCRYN) New Delhi', + parentMapId: '124026', + sbOrgId: '', + sbOrgSubType: '', + sbOrgType: '', }, { - "mapId": "127516", - "orgCode": "O74", - "orgName": "Homoeopathic Pharmacopoeia Laboratory (HPL) Ghaziabad Uttar Pradesh", - "parentMapId": "124026", - "sbOrgId": "", - "sbOrgSubType": "", - "sbOrgType": "" - } + mapId: '127516', + orgCode: 'O74', + orgName: 'Homoeopathic Pharmacopoeia Laboratory (HPL) Ghaziabad Uttar Pradesh', + parentMapId: '124026', + sbOrgId: '', + sbOrgSubType: '', + sbOrgType: '', + }, ] - var response = { + const response = { id: 'api.org.profile.read', params: { status: 'SUCCESS', @@ -358,32 +358,32 @@ proxiesV8.get('/org/v1/list/:type', async (req, res) => { responseCode: 'OK', result: { response: { - } - } + }, + }, } switch (type) { case 'SPV': response.result.response[content] = spvList response.result.response[count] = spvList.length - break; + break case '123881': response.result.response[content] = minList123881 response.result.response[count] = minList123881.length - break; + break case '123882': response.result.response[content] = minList123882 response.result.response[count] = minList123882.length - break; + break case '124023': response.result.response[content] = orgList124023 response.result.response[count] = orgList124023.length - break; + break case '124026': response.result.response[content] = orgList124026 response.result.response[count] = orgList124026.length - break; + break default: - break; + break } res.status(200).send(response) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 70d243a5..3f974f34 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -840,7 +840,7 @@ export const API_LIST = { ROLE.WAT_MEMBER, ROLE.CBC_ADMIN, ROLE.SPV_ADMIN, - ROLE.STATE_ADMIN + ROLE.STATE_ADMIN, ], }, '/protected/v8/workallocation/getWorkOrders': { From eb387adb3db57e760e9c6a1676f037c1664255f2 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Fri, 17 Jun 2022 20:27:06 +0530 Subject: [PATCH 332/856] Updated org list API as public --- src/proxies_v8/proxies_v8.ts | 135 -------------------------------- src/publicApi_v8/publicApiV8.ts | 133 +++++++++++++++++++++++++++++++ src/utils/whitelistApis.ts | 8 -- 3 files changed, 133 insertions(+), 143 deletions(-) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 83fac4b8..aeaf2443 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -254,141 +254,6 @@ proxiesV8.get('/org/v1/profile/read/:orgId', async (req, res) => { res.status(200).send(response) }) -proxiesV8.get('/org/v1/list/:type', async (req, res) => { - const type = req.params.type - const content = 'content' - const count = 'count' - const spvList = [ - { - mapId: '123881', - orgCode: 'M1', - orgName: 'Ministry of Agriculture and Farmers Welfare', - parentMapId: 'SPV', - sbOrgId: '', - sbOrgSubType: '', - sbOrgType: '', - }, - { - mapId: '123882', - orgCode: 'M2', - orgName: 'Ministry of AYUSH', - parentMapId: 'SPV', - sbOrgId: '', - sbOrgSubType: '', - sbOrgType: '', - }, - ] - const minList123881 = [ - { - mapId: '124023', - orgCode: 'D1', - orgName: 'Department of Agricultural Research and Education (DARE)', - parentMapId: '123881', - sbOrgId: '', - sbOrgSubType: '', - sbOrgType: '', - }, - { - mapId: '124024', - orgCode: 'D2', - orgName: 'Department of Agriculture Cooperation and Farmers Welfare', - parentMapId: '123881', - sbOrgId: '', - sbOrgSubType: '', - sbOrgType: '', - }, - ] - const minList123882 = [ - { - mapId: '124026', - orgCode: '', - orgName: 'NA', - parentMapId: '123882', - sbOrgId: '', - sbOrgSubType: '', - sbOrgType: '', - }, - ] - const orgList124023 = [ - { - mapId: '127443', - orgCode: 'O1', - orgName: 'Agricultural Technology Information Centre (ATIC)', - parentMapId: '124023', - sbOrgId: '', - sbOrgSubType: '', - sbOrgType: '', - }, - { - mapId: '127444', - orgCode: 'O2', - orgName: 'Agrinnovate India', - parentMapId: '124023', - sbOrgId: '', - sbOrgSubType: '', - sbOrgType: '', - }, - ] - const orgList124026 = [ - { - mapId: '127515', - orgCode: 'O73', - orgName: 'Central Council for Research in Yoga & Naturopathy (CCRYN) New Delhi', - parentMapId: '124026', - sbOrgId: '', - sbOrgSubType: '', - sbOrgType: '', - }, - { - mapId: '127516', - orgCode: 'O74', - orgName: 'Homoeopathic Pharmacopoeia Laboratory (HPL) Ghaziabad Uttar Pradesh', - parentMapId: '124026', - sbOrgId: '', - sbOrgSubType: '', - sbOrgType: '', - }, - ] - - const response = { - id: 'api.org.profile.read', - params: { - status: 'SUCCESS', - }, - responseCode: 'OK', - result: { - response: { - }, - }, - } - switch (type) { - case 'SPV': - response.result.response[content] = spvList - response.result.response[count] = spvList.length - break - case '123881': - response.result.response[content] = minList123881 - response.result.response[count] = minList123881.length - break - case '123882': - response.result.response[content] = minList123882 - response.result.response[count] = minList123882.length - break - case '124023': - response.result.response[content] = orgList124023 - response.result.response[count] = orgList124023.length - break - case '124026': - response.result.response[content] = orgList124026 - response.result.response[count] = orgList124026.length - break - default: - break - } - - res.status(200).send(response) -}) - proxiesV8.use('/org/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) diff --git a/src/publicApi_v8/publicApiV8.ts b/src/publicApi_v8/publicApiV8.ts index 597d6b0c..73dc5796 100644 --- a/src/publicApi_v8/publicApiV8.ts +++ b/src/publicApi_v8/publicApiV8.ts @@ -15,3 +15,136 @@ publicApiV8.use('/assets', proxyCreatorRoute(express.Router(), CONSTANTS.WEB_HOST_PROXY + '/web-hosted/web-client-public-assets')) publicApiV8.use('/workallocation', workallocationPublic) + +publicApiV8.get(['/org/v1/list', '/org/v1/list/:type'], async (req, res) => { + const type = req.params.type + const content = 'content' + const count = 'count' + const spvList = [ + { + mapId: '123881', + orgCode: 'M1', + orgName: 'Ministry of Agriculture and Farmers Welfare', + parentMapId: 'SPV', + sbOrgId: '', + sbOrgSubType: '', + sbOrgType: '', + }, + { + mapId: '123882', + orgCode: 'M2', + orgName: 'Ministry of AYUSH', + parentMapId: 'SPV', + sbOrgId: '', + sbOrgSubType: '', + sbOrgType: '', + }, + ] + const minList123881 = [ + { + mapId: '124023', + orgCode: 'D1', + orgName: 'Department of Agricultural Research and Education (DARE)', + parentMapId: '123881', + sbOrgId: '', + sbOrgSubType: '', + sbOrgType: '', + }, + { + mapId: '124024', + orgCode: 'D2', + orgName: 'Department of Agriculture Cooperation and Farmers Welfare', + parentMapId: '123881', + sbOrgId: '', + sbOrgSubType: '', + sbOrgType: '', + }, + ] + const minList123882 = [ + { + mapId: '124026', + orgCode: '', + orgName: 'NA', + parentMapId: '123882', + sbOrgId: '', + sbOrgSubType: '', + sbOrgType: '', + }, + ] + const orgList124023 = [ + { + mapId: '127443', + orgCode: 'O1', + orgName: 'Agricultural Technology Information Centre (ATIC)', + parentMapId: '124023', + sbOrgId: '', + sbOrgSubType: '', + sbOrgType: '', + }, + { + mapId: '127444', + orgCode: 'O2', + orgName: 'Agrinnovate India', + parentMapId: '124023', + sbOrgId: '', + sbOrgSubType: '', + sbOrgType: '', + }, + ] + const orgList124026 = [ + { + mapId: '127515', + orgCode: 'O73', + orgName: 'Central Council for Research in Yoga & Naturopathy (CCRYN) New Delhi', + parentMapId: '124026', + sbOrgId: '', + sbOrgSubType: '', + sbOrgType: '', + }, + { + mapId: '127516', + orgCode: 'O74', + orgName: 'Homoeopathic Pharmacopoeia Laboratory (HPL) Ghaziabad Uttar Pradesh', + parentMapId: '124026', + sbOrgId: '', + sbOrgSubType: '', + sbOrgType: '', + }, + ] + + const response = { + id: 'api.org.profile.read', + params: { + status: 'SUCCESS', + }, + responseCode: 'OK', + result: { + response: { + }, + }, + } + switch (type) { + case '123881': + response.result.response[content] = minList123881 + response.result.response[count] = minList123881.length + break + case '123882': + response.result.response[content] = minList123882 + response.result.response[count] = minList123882.length + break + case '124023': + response.result.response[content] = orgList124023 + response.result.response[count] = orgList124023.length + break + case '124026': + response.result.response[content] = orgList124026 + response.result.response[count] = orgList124026.length + break + default: + response.result.response[content] = spvList + response.result.response[count] = spvList.length + break + } + + res.status(200).send(response) +}) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 3f974f34..36b5c20c 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1771,13 +1771,6 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/proxies/v8/org/v1/list/:type': { - checksNeeded: [CHECK.ROLE], - // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ - ROLE.PUBLIC, - ], - }, }, URL_PATTERN: [ @@ -2013,6 +2006,5 @@ export const API_LIST = { '/proxies/v8/ratings/v1/summary/:activityId/:activityType', '/proxies/v8/ratings/v1/ratingLookUp', '/proxies/v8/user/v1/positions', - '/proxies/v8/org/v1/list/:type', ], } From 0fe78514cacd33a6185163671407adcd0502398d Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Tue, 21 Jun 2022 13:24:35 +0530 Subject: [PATCH 333/856] Added API for org on-boarding --- src/publicApi_v8/publicApiV8.ts | 150 +++++--------------------------- src/utils/whitelistApis.ts | 20 +++++ 2 files changed, 43 insertions(+), 127 deletions(-) diff --git a/src/publicApi_v8/publicApiV8.ts b/src/publicApi_v8/publicApiV8.ts index 73dc5796..21f55b05 100644 --- a/src/publicApi_v8/publicApiV8.ts +++ b/src/publicApi_v8/publicApiV8.ts @@ -1,10 +1,18 @@ +import axios from 'axios' import express from 'express' +import { axiosRequestConfig } from '../configs/request.config' import { CONSTANTS } from '../utils/env' +import { logError } from '../utils/logger' import { proxyCreatorRoute } from '../utils/proxyCreator' import { workallocationPublic } from './workallocationPublic' export const publicApiV8 = express.Router() +const API_END_POINTS = { + kongOrgList: `${CONSTANTS.KONG_API_BASE}/org/v1/list`, + kongOrgListWithId: (mapId: string) => `${CONSTANTS.KONG_API_BASE}/org/v1/list/${mapId}`, +} + publicApiV8.get('/', (_req, res) => { res.json({ status: `Public Api is working fine https base: ${CONSTANTS.HTTPS_HOST}`, @@ -18,133 +26,21 @@ publicApiV8.use('/workallocation', workallocationPublic) publicApiV8.get(['/org/v1/list', '/org/v1/list/:type'], async (req, res) => { const type = req.params.type - const content = 'content' - const count = 'count' - const spvList = [ - { - mapId: '123881', - orgCode: 'M1', - orgName: 'Ministry of Agriculture and Farmers Welfare', - parentMapId: 'SPV', - sbOrgId: '', - sbOrgSubType: '', - sbOrgType: '', - }, - { - mapId: '123882', - orgCode: 'M2', - orgName: 'Ministry of AYUSH', - parentMapId: 'SPV', - sbOrgId: '', - sbOrgSubType: '', - sbOrgType: '', - }, - ] - const minList123881 = [ - { - mapId: '124023', - orgCode: 'D1', - orgName: 'Department of Agricultural Research and Education (DARE)', - parentMapId: '123881', - sbOrgId: '', - sbOrgSubType: '', - sbOrgType: '', - }, - { - mapId: '124024', - orgCode: 'D2', - orgName: 'Department of Agriculture Cooperation and Farmers Welfare', - parentMapId: '123881', - sbOrgId: '', - sbOrgSubType: '', - sbOrgType: '', - }, - ] - const minList123882 = [ - { - mapId: '124026', - orgCode: '', - orgName: 'NA', - parentMapId: '123882', - sbOrgId: '', - sbOrgSubType: '', - sbOrgType: '', - }, - ] - const orgList124023 = [ - { - mapId: '127443', - orgCode: 'O1', - orgName: 'Agricultural Technology Information Centre (ATIC)', - parentMapId: '124023', - sbOrgId: '', - sbOrgSubType: '', - sbOrgType: '', - }, - { - mapId: '127444', - orgCode: 'O2', - orgName: 'Agrinnovate India', - parentMapId: '124023', - sbOrgId: '', - sbOrgSubType: '', - sbOrgType: '', - }, - ] - const orgList124026 = [ - { - mapId: '127515', - orgCode: 'O73', - orgName: 'Central Council for Research in Yoga & Naturopathy (CCRYN) New Delhi', - parentMapId: '124026', - sbOrgId: '', - sbOrgSubType: '', - sbOrgType: '', - }, - { - mapId: '127516', - orgCode: 'O74', - orgName: 'Homoeopathic Pharmacopoeia Laboratory (HPL) Ghaziabad Uttar Pradesh', - parentMapId: '124026', - sbOrgId: '', - sbOrgSubType: '', - sbOrgType: '', - }, - ] - - const response = { - id: 'api.org.profile.read', - params: { - status: 'SUCCESS', - }, - responseCode: 'OK', - result: { - response: { - }, - }, + let urlValue = '' + if (type) { + urlValue = API_END_POINTS.kongOrgListWithId(type) + } else { + urlValue = API_END_POINTS.kongOrgList } - switch (type) { - case '123881': - response.result.response[content] = minList123881 - response.result.response[count] = minList123881.length - break - case '123882': - response.result.response[content] = minList123882 - response.result.response[count] = minList123882.length - break - case '124023': - response.result.response[content] = orgList124023 - response.result.response[count] = orgList124023.length - break - case '124026': - response.result.response[content] = orgList124026 - response.result.response[count] = orgList124026.length - break - default: - response.result.response[content] = spvList - response.result.response[count] = spvList.length - break + try { + const sbUserReadResponse = await axios({ + ...axiosRequestConfig, + method: 'GET', + url: urlValue, + }) + res.status(200).send(sbUserReadResponse) + } catch (err) { + logError(err) + res.status(500).send(err) } - - res.status(200).send(response) }) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 36b5c20c..ed869603 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -410,6 +410,16 @@ export const API_LIST = { ROLE.STATE_ADMIN, ], }, + '/proxies/v8/org/v1/profile/patch': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.CBC_ADMIN, + ROLE.MDO_ADMIN, + ROLE.SPV_ADMIN, + ROLE.STATE_ADMIN, + ], + }, '/proxies/v8/org/v1/update': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1771,6 +1781,14 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/org/ext/v1/create': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.SPV_ADMIN, + ROLE.STATE_ADMIN, + ], + }, }, URL_PATTERN: [ @@ -1830,6 +1848,7 @@ export const API_LIST = { '/proxies/v8/user/v1/search', '/proxies/v8/org/v1/read', '/proxies/v8/org/v1/profile/read/:orgId', + '/proxies/v8/org/v1/profile/patch', '/proxies/v8/org/v1/create', '/proxies/v8/discussion/tags', '/proxies/v8/user/v1/block', @@ -2006,5 +2025,6 @@ export const API_LIST = { '/proxies/v8/ratings/v1/summary/:activityId/:activityType', '/proxies/v8/ratings/v1/ratingLookUp', '/proxies/v8/user/v1/positions', + '/proxies/v8/org/ext/v1/create', ], } From cdc7d320abeb560eafb92d4cd8480edee622b1f8 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Tue, 21 Jun 2022 13:59:40 +0530 Subject: [PATCH 334/856] Fix for org list API --- src/publicApi_v8/publicApiV8.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/publicApi_v8/publicApiV8.ts b/src/publicApi_v8/publicApiV8.ts index 21f55b05..e12c4c88 100644 --- a/src/publicApi_v8/publicApiV8.ts +++ b/src/publicApi_v8/publicApiV8.ts @@ -35,6 +35,9 @@ publicApiV8.get(['/org/v1/list', '/org/v1/list/:type'], async (req, res) => { try { const sbUserReadResponse = await axios({ ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + }, method: 'GET', url: urlValue, }) From 256fb8915f53b8b77517c64be11dab257850129e Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Tue, 21 Jun 2022 14:56:51 +0530 Subject: [PATCH 335/856] Fixed org list API issue --- src/publicApi_v8/publicApiV8.ts | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/src/publicApi_v8/publicApiV8.ts b/src/publicApi_v8/publicApiV8.ts index e12c4c88..b62d400e 100644 --- a/src/publicApi_v8/publicApiV8.ts +++ b/src/publicApi_v8/publicApiV8.ts @@ -1,18 +1,10 @@ -import axios from 'axios' import express from 'express' -import { axiosRequestConfig } from '../configs/request.config' import { CONSTANTS } from '../utils/env' -import { logError } from '../utils/logger' import { proxyCreatorRoute } from '../utils/proxyCreator' import { workallocationPublic } from './workallocationPublic' export const publicApiV8 = express.Router() -const API_END_POINTS = { - kongOrgList: `${CONSTANTS.KONG_API_BASE}/org/v1/list`, - kongOrgListWithId: (mapId: string) => `${CONSTANTS.KONG_API_BASE}/org/v1/list/${mapId}`, -} - publicApiV8.get('/', (_req, res) => { res.json({ status: `Public Api is working fine https base: ${CONSTANTS.HTTPS_HOST}`, @@ -24,26 +16,4 @@ publicApiV8.use('/assets', publicApiV8.use('/workallocation', workallocationPublic) -publicApiV8.get(['/org/v1/list', '/org/v1/list/:type'], async (req, res) => { - const type = req.params.type - let urlValue = '' - if (type) { - urlValue = API_END_POINTS.kongOrgListWithId(type) - } else { - urlValue = API_END_POINTS.kongOrgList - } - try { - const sbUserReadResponse = await axios({ - ...axiosRequestConfig, - headers: { - Authorization: CONSTANTS.SB_API_KEY, - }, - method: 'GET', - url: urlValue, - }) - res.status(200).send(sbUserReadResponse) - } catch (err) { - logError(err) - res.status(500).send(err) - } -}) +publicApiV8.use('/org/v1/list', proxyCreatorRoute(express.Router(), CONSTANTS.KONG_API_BASE + '/org/v1/list')) From 2ce777ee2c0fa9057acd41b260f398f77f8e47c4 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Wed, 22 Jun 2022 12:12:04 +0530 Subject: [PATCH 336/856] Removed temp handling for org profile read API --- src/proxies_v8/proxies_v8.ts | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index aeaf2443..bbd0f7b8 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -225,35 +225,6 @@ proxiesV8.use('/notification/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) -proxiesV8.get('/org/v1/profile/read/:orgId', async (req, res) => { - const orgId = req.params.orgId - const response = { - id: 'api.org.profile.read', - params: { - status: 'SUCCESS', - }, - responseCode: 'OK', - result: { - response: { - organisationId: orgId, - profile: { - consultancy: {}, - faculty: {}, - infrastructure: {}, - instituteProfile: {}, - platformWalkthrough: {}, - research: {}, - rolesAndFunctions: {}, - trainingPrograms: {}, - }, - }, - }, - - ver: 'v1', - } - res.status(200).send(response) -}) - proxiesV8.use('/org/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) From 885f8f9bcf4e1630312b2ebd76884cacd39baa57 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Wed, 22 Jun 2022 15:18:38 +0530 Subject: [PATCH 337/856] Updated whitelist API for org profile read --- src/utils/whitelistApis.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index ed869603..42bca025 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -400,7 +400,7 @@ export const API_LIST = { ROLE.STATE_ADMIN, ], }, - '/proxies/v8/org/v1/profile/read/:orgId': { + '/proxies/v8/org/v1/profile/read': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -1847,7 +1847,7 @@ export const API_LIST = { '/proxies/v8/user/private/v1/assign/role', '/proxies/v8/user/v1/search', '/proxies/v8/org/v1/read', - '/proxies/v8/org/v1/profile/read/:orgId', + '/proxies/v8/org/v1/profile/read', '/proxies/v8/org/v1/profile/patch', '/proxies/v8/org/v1/create', '/proxies/v8/discussion/tags', From 45062c5f9de3d4640a5f6fd81cf6c2534a32424c Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 23 Jun 2022 11:28:34 +0530 Subject: [PATCH 338/856] Added restriction for orgTypeList for STATE_ADMIN role --- src/proxies_v8/proxies_v8.ts | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index bbd0f7b8..889a0b5a 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -2,6 +2,7 @@ import axios from 'axios' import express from 'express' import { UploadedFile } from 'express-fileupload' import FormData from 'form-data' +import lodash from 'lodash' import { axiosRequestConfig } from '../configs/request.config' import { CONSTANTS } from '../utils/env' import { logInfo } from '../utils/logger' @@ -25,6 +26,7 @@ import { extractUserIdFromRequest, extractUserToken } from '../utils/requestExtr const API_END_POINTS = { contentNotificationEmail: `${CONSTANTS.NOTIFICATION_SERVIC_API_BASE}/v1/notification/send/sync`, + orgTypeListEndPoint: `${CONSTANTS.KONG_API_BASE}/data/v1/system/settings/get/orgTypeList`, } export const proxiesV8 = express.Router() @@ -51,6 +53,7 @@ proxiesV8.post('/upload/*', (req, res) => { Authorization: CONSTANTS.SB_API_KEY, org: 'dopt', rootorg: 'igot', + // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req), 'x-authenticated-userid': extractUserIdFromRequest(req), }, @@ -94,6 +97,7 @@ proxiesV8.post('/private/upload/*', (_req, _res) => { Authorization: CONSTANTS.SB_API_KEY, org: 'dopt', rootorg: 'igot', + // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(_req), 'x-authenticated-userid': extractUserIdFromRequest(_req), }, @@ -281,6 +285,36 @@ proxiesV8.use('/wat/dashboard/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.DASHBOARD_API_BASE}`) ) +proxiesV8.get('/data/v1/system/settings/get/orgTypeList', async (req, res) => { + const roleData = lodash.get(req, 'session.userRoles') + logInfo('orgTypeList API call : Users Roles are...') + logInfo(roleData) + const response = await axios({ + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), + }, + method: 'GET', + url: API_END_POINTS.orgTypeListEndPoint, + }) + if (roleData.includes('STATE_ADMIN')) { + const hiddenList = ['CBC', 'CBP', 'STATE'] + const orgTypeListObj = JSON.parse(response.data.result.response.value) + const orgTypeList = orgTypeListObj.orgTypeList + // tslint:disable-next-line: no-any + orgTypeList.forEach((element: any) => { + if (hiddenList.includes(element.name)) { + element.isHidden = true + } + }) + orgTypeListObj.orgTypeList = orgTypeList + response.data.result.response.value = JSON.stringify(orgTypeListObj) + } + res.status(200).send(response.data) +}) + proxiesV8.use('/data/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) From aee1bf5f8b646b9113b4b3bb8c16721926068eb5 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 23 Jun 2022 14:05:26 +0530 Subject: [PATCH 339/856] Added limitation in org search API for StateAdmin role --- src/proxies_v8/proxies_v8.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 889a0b5a..d1f6c173 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -26,6 +26,7 @@ import { extractUserIdFromRequest, extractUserToken } from '../utils/requestExtr const API_END_POINTS = { contentNotificationEmail: `${CONSTANTS.NOTIFICATION_SERVIC_API_BASE}/v1/notification/send/sync`, + kongSearchOrg: `${CONSTANTS.KONG_API_BASE}/org/v1/search`, orgTypeListEndPoint: `${CONSTANTS.KONG_API_BASE}/data/v1/system/settings/get/orgTypeList`, } @@ -229,6 +230,26 @@ proxiesV8.use('/notification/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) +proxiesV8.post('/org/v1/search', async (req, res) => { + const roleData = lodash.get(req, 'session.userRoles') + const rootOrgId = lodash.get(req, 'session.rootOrgId') + if (roleData.includes('STATE_ADMIN')) { + req.body.request.filters.externalId = rootOrgId + } + const searchResponse = await axios({ + ...axiosRequestConfig, + data: req.body, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), + }, + method: 'POST', + url: API_END_POINTS.kongSearchOrg, + }) + res.status(200).send(searchResponse.data) +}) + proxiesV8.use('/org/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) From 348e675c90b5591a439e46ab8bbcc167604cd253 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Fri, 24 Jun 2022 14:15:36 +0530 Subject: [PATCH 340/856] Updated org search API for STATE_ADMIN role --- src/proxies_v8/proxies_v8.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index d1f6c173..68efd2c6 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -26,6 +26,7 @@ import { extractUserIdFromRequest, extractUserToken } from '../utils/requestExtr const API_END_POINTS = { contentNotificationEmail: `${CONSTANTS.NOTIFICATION_SERVIC_API_BASE}/v1/notification/send/sync`, + kongExtOrgSearch: `${CONSTANTS.KONG_API_BASE}/org/v1/cb/ext/search`, kongSearchOrg: `${CONSTANTS.KONG_API_BASE}/org/v1/search`, orgTypeListEndPoint: `${CONSTANTS.KONG_API_BASE}/data/v1/system/settings/get/orgTypeList`, } @@ -233,8 +234,10 @@ proxiesV8.use('/notification/*', proxiesV8.post('/org/v1/search', async (req, res) => { const roleData = lodash.get(req, 'session.userRoles') const rootOrgId = lodash.get(req, 'session.rootOrgId') + let urlPath = API_END_POINTS.kongSearchOrg if (roleData.includes('STATE_ADMIN')) { - req.body.request.filters.externalId = rootOrgId + req.body.request.filters.sbRootOrgId = rootOrgId + urlPath = API_END_POINTS.kongExtOrgSearch } const searchResponse = await axios({ ...axiosRequestConfig, @@ -245,7 +248,7 @@ proxiesV8.post('/org/v1/search', async (req, res) => { 'x-authenticated-user-token': extractUserToken(req), }, method: 'POST', - url: API_END_POINTS.kongSearchOrg, + url: urlPath, }) res.status(200).send(searchResponse.data) }) From b83858d3e58dcb257dafc26f5856cecaac592f50 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Fri, 24 Jun 2022 17:21:44 +0530 Subject: [PATCH 341/856] Added STATE_ADMIN role for create user API --- src/utils/whitelistApis.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 42bca025..1ab08dc2 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -778,6 +778,7 @@ export const API_LIST = { ROLE.SPV_ADMIN, ROLE.CBP_ADMIN, ROLE.CONTENT_CREATOR, + ROLE.STATE_ADMIN, ], }, '/protected/v8/user/profileRegistry/getUserRegistryByUser/:id': { From 7e31a9e3ef661ed3886e1c396d356b5f2d6f6527 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Tue, 5 Jul 2022 17:10:46 +0530 Subject: [PATCH 342/856] Added v1/content/copy API --- src/proxies_v8/proxies_v8.ts | 4 ++++ src/utils/whitelistApis.ts | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 68efd2c6..d889aa63 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -179,6 +179,10 @@ proxiesV8.use('/v1/content/retire', proxyCreatorKnowledge(express.Router(), `${CONSTANTS.KNOWLEDGE_MW_API_BASE}`) ) +proxiesV8.use('/v1/content/copy', + proxyCreatorKnowledge(express.Router(), `${CONSTANTS.KNOWLEDGE_MW_API_BASE}`) +) + proxiesV8.use('/private/content/*', proxyContent(express.Router(), `${CONSTANTS.CONTENT_SERVICE_API_BASE}`) ) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 1ab08dc2..4d5c0571 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -321,6 +321,13 @@ export const API_LIST = { ROLE.EDITOR, ], }, + '/proxies/v8/v1/content/copy': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.CONTENT_CREATOR, + ], + }, '/proxies/v8/action/content/v3/updateReviewStatus/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1839,6 +1846,7 @@ export const API_LIST = { '/proxies/v8/action/content/v3/create', '/proxies/v8/upload/action/content/v3/upload/:do_id', '/proxies/v8/v1/content/retire', + '/proxies/v8/v1/content/copy', '/proxies/v8/action/content/v3/updateReviewStatus/:do_id', '/proxies/v8/action/content/v3/review/:do_id', '/proxies/v8/discussion/recent', From 595df5d54f49ac09d9ee7eb689baf69dd26c5d29 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Tue, 5 Jul 2022 17:21:56 +0530 Subject: [PATCH 343/856] Added v1/content/copy API --- src/proxies_v8/proxies_v8.ts | 2 +- src/utils/whitelistApis.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index d889aa63..8949da07 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -179,7 +179,7 @@ proxiesV8.use('/v1/content/retire', proxyCreatorKnowledge(express.Router(), `${CONSTANTS.KNOWLEDGE_MW_API_BASE}`) ) -proxiesV8.use('/v1/content/copy', +proxiesV8.use('/v1/content/copy/*', proxyCreatorKnowledge(express.Router(), `${CONSTANTS.KNOWLEDGE_MW_API_BASE}`) ) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 4d5c0571..cd513217 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -321,7 +321,7 @@ export const API_LIST = { ROLE.EDITOR, ], }, - '/proxies/v8/v1/content/copy': { + '/proxies/v8/v1/content/copy/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -1846,7 +1846,7 @@ export const API_LIST = { '/proxies/v8/action/content/v3/create', '/proxies/v8/upload/action/content/v3/upload/:do_id', '/proxies/v8/v1/content/retire', - '/proxies/v8/v1/content/copy', + '/proxies/v8/v1/content/copy/:do_id', '/proxies/v8/action/content/v3/updateReviewStatus/:do_id', '/proxies/v8/action/content/v3/review/:do_id', '/proxies/v8/discussion/recent', From 5de61f8eceb65de2cb8e9616e538be1fc0a652a5 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Wed, 6 Jul 2022 15:07:10 +0530 Subject: [PATCH 344/856] updated content copy API --- src/utils/whitelistApis.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index cd513217..01f4c904 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -321,7 +321,7 @@ export const API_LIST = { ROLE.EDITOR, ], }, - '/proxies/v8/v1/content/copy/:do_id': { + '/proxies/v8/action/content/v3/copy/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -1846,7 +1846,7 @@ export const API_LIST = { '/proxies/v8/action/content/v3/create', '/proxies/v8/upload/action/content/v3/upload/:do_id', '/proxies/v8/v1/content/retire', - '/proxies/v8/v1/content/copy/:do_id', + '/proxies/v8/action/content/v3/copy/:do_id', '/proxies/v8/action/content/v3/updateReviewStatus/:do_id', '/proxies/v8/action/content/v3/review/:do_id', '/proxies/v8/discussion/recent', From be1ca43a345f0763f92a5ca4acab9fab774c1571 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Sat, 30 Jul 2022 07:09:28 +0530 Subject: [PATCH 345/856] 4.8.0 o auth (#105) * Added OAuth for Parichay * added user basicInfo API * Added API for basicProfileUpdate * Added Form Service APIs * Added parichay revoke API handling * Added functionality to handle user blocked case in user read API Co-authored-by: Darshan Nagesh Co-authored-by: Haritest --- package-lock.json | 352 +++++++++++++++++++++++++- package.json | 4 + src/configs/keycloak.config.ts | 12 + src/proxies_v8/proxies_v8.ts | 51 +++- src/publicApi_v8/googleOAuthHelper.ts | 87 +++++++ src/publicApi_v8/googleauth.ts | 77 ++++++ src/publicApi_v8/keycloakHelper.ts | 77 ++++++ src/publicApi_v8/parichayAuth.ts | 99 ++++++++ src/publicApi_v8/ssoUserHelper.ts | 180 +++++++++++++ src/utils/custom-keycloak.ts | 32 ++- src/utils/env.ts | 19 ++ src/utils/jwtHelper.ts | 33 +++ src/utils/permissionHelper.ts | 9 +- src/utils/proxyCreator.ts | 3 - src/utils/whitelistApis.ts | 48 ++++ 15 files changed, 1069 insertions(+), 14 deletions(-) create mode 100644 src/publicApi_v8/googleOAuthHelper.ts create mode 100644 src/publicApi_v8/googleauth.ts create mode 100644 src/publicApi_v8/keycloakHelper.ts create mode 100644 src/publicApi_v8/parichayAuth.ts create mode 100644 src/publicApi_v8/ssoUserHelper.ts create mode 100644 src/utils/jwtHelper.ts diff --git a/package-lock.json b/package-lock.json index 6f24b6c7..ff126ca8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -398,6 +398,14 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -416,6 +424,29 @@ "printj": "~1.1.0" } }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "ajv": { "version": "6.10.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", @@ -649,6 +680,11 @@ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" }, + "arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==" + }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -858,8 +894,12 @@ "base64-js": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", - "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==", - "dev": true + "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" + }, + "base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==" }, "basic-auth": { "version": "2.0.1", @@ -877,6 +917,11 @@ "tweetnacl": "^0.14.3" } }, + "bignumber.js": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.2.tgz", + "integrity": "sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw==" + }, "binary-extensions": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", @@ -1030,6 +1075,11 @@ "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", "dev": true }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "buffer-fill": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", @@ -2240,6 +2290,14 @@ "safer-buffer": "^2.1.0" } }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -2360,6 +2418,11 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, + "event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" + }, "eventemitter3": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", @@ -2714,6 +2777,11 @@ "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" }, + "fast-text-encoding": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fast-text-encoding/-/fast-text-encoding-1.0.4.tgz", + "integrity": "sha512-x6lDDm/tBAzX9kmsPcZsNbvDs3Zey3+scsxaZElS8xWLgUMAg/oFLeewfUz0mu1CblHhhsu15jGkraldkFh8KQ==" + }, "fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -3502,6 +3570,34 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "gaxios": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.3.4.tgz", + "integrity": "sha512-US8UMj8C5pRnao3Zykc4AAVr+cffoNKRTg9Rsf2GiuZCW69vgJj38VK2PzlPuQU73FZ/nTk9/Av6/JGcE1N9vA==", + "requires": { + "abort-controller": "^3.0.0", + "extend": "^3.0.2", + "https-proxy-agent": "^5.0.0", + "is-stream": "^2.0.0", + "node-fetch": "^2.3.0" + }, + "dependencies": { + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==" + } + } + }, + "gcp-metadata": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-3.5.0.tgz", + "integrity": "sha512-ZQf+DLZ5aKcRpLzYUyBS3yo3N0JSa82lNDO8rj3nMSlovLcz2riKFBsYgDzeXcv75oo5eqB2lx+B14UvPoCRnA==", + "requires": { + "gaxios": "^2.1.0", + "json-bigint": "^0.3.0" + } + }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", @@ -3677,6 +3773,74 @@ "sparkles": "^1.0.0" } }, + "google-auth-library": { + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-5.10.1.tgz", + "integrity": "sha512-rOlaok5vlpV9rSiUu5EpR0vVpc+PhN62oF4RyX/6++DG1VsaulAFEMlDYBLjJDDPI6OcNOCGAKy9UVB/3NIDXg==", + "requires": { + "arrify": "^2.0.0", + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "fast-text-encoding": "^1.0.0", + "gaxios": "^2.1.0", + "gcp-metadata": "^3.4.0", + "gtoken": "^4.1.0", + "jws": "^4.0.0", + "lru-cache": "^5.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } + }, + "google-p12-pem": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-2.0.5.tgz", + "integrity": "sha512-7RLkxwSsMsYh9wQ5Vb2zRtkAHvqPvfoMGag+nugl1noYO7gf0844Yr9TIFA5NEBMAeVt2Z+Imu7CQMp3oNatzQ==", + "requires": { + "node-forge": "^0.10.0" + } + }, + "googleapis": { + "version": "45.0.0", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-45.0.0.tgz", + "integrity": "sha512-v5+4Cw+MnG3z9FRNTdOCcOWBvzhp0zcbapI3D1E1ndykIRA7tE8oflSBRyyVpX6BDhRGSqNu2lU8G15Fe+Ih4g==", + "requires": { + "google-auth-library": "^5.2.0", + "googleapis-common": "^3.1.0" + } + }, + "googleapis-common": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-3.2.2.tgz", + "integrity": "sha512-sTEXlauVce4eMX0S6hVoiWgxVzQZ7dc16KcGF7eh+A+uIyDgXqnuwOMZw+svX4gOJv6w4ACecm23Qh9UDaldsw==", + "requires": { + "extend": "^3.0.2", + "gaxios": "^2.0.1", + "google-auth-library": "^5.6.1", + "qs": "^6.7.0", + "url-template": "^2.0.8", + "uuid": "^7.0.0" + }, + "dependencies": { + "uuid": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", + "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==" + } + } + }, "got": { "version": "6.7.1", "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", @@ -3707,6 +3871,24 @@ "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", "dev": true }, + "gtoken": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-4.1.4.tgz", + "integrity": "sha512-VxirzD0SWoFUo5p8RDP8Jt2AGyOmyYcT/pOUgDKJCK+iSw0TMqwrVfY37RXTNmoKwrzmDHSk0GMT9FsgVmnVSA==", + "requires": { + "gaxios": "^2.1.0", + "google-p12-pem": "^2.0.0", + "jws": "^4.0.0", + "mime": "^2.2.0" + }, + "dependencies": { + "mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==" + } + } + }, "gulp": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", @@ -4033,6 +4215,30 @@ "sshpk": "^1.7.0" } }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "requires": { + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, "husky": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/husky/-/husky-2.3.0.tgz", @@ -4568,6 +4774,14 @@ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, + "json-bigint": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.1.tgz", + "integrity": "sha512-DGWnSzmusIreWlEupsUelHrhwmPPE+FiQvg+drKfk2p+bdEYa5mp4PJ8JsCWqae0M2jQNb0HPvnwvf1qOTThzQ==", + "requires": { + "bignumber.js": "^9.0.0" + } + }, "json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", @@ -4606,6 +4820,49 @@ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" }, + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "dependencies": { + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -4623,6 +4880,16 @@ "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=", "dev": true }, + "jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, "jwk-to-pem": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/jwk-to-pem/-/jwk-to-pem-2.0.5.tgz", @@ -4633,6 +4900,20 @@ "safe-buffer": "^5.0.1" } }, + "jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "requires": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "jwt-decode": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz", + "integrity": "sha512-86GgN2vzfUu7m9Wcj63iUkuDzFNYFVmjeDm2GzWpUk+opB0pEpMsw6ePCMrhYkumz2C1ihqtZzOMAg7FiXcNoQ==" + }, "keycloak-admin": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/keycloak-admin/-/keycloak-admin-1.13.0.tgz", @@ -4793,6 +5074,41 @@ "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", "dev": true }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", @@ -5098,6 +5414,19 @@ "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.1.0.tgz", "integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==" }, + "node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==" + }, "node-xlsx": { "version": "0.15.0", "resolved": "https://registry.npmjs.org/node-xlsx/-/node-xlsx-0.15.0.tgz", @@ -6979,6 +7308,11 @@ } } }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "trim-repeated": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", @@ -7500,6 +7834,20 @@ } } }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", diff --git a/package.json b/package.json index dedfb377..223727b1 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@types/node-xlsx": "^0.15.0", "async": "^3.2.0", "axios": "^0.19.1", + "base64url": "^3.0.1", "cassandra-driver": "^4.1.0", "cassandra-store": "^5.0.0", "chalk": "^2.4.2", @@ -38,8 +39,11 @@ "express-healthcheck": "^0.1.0", "express-session": "^1.16.1", "form-data": "^3.0.0", + "googleapis": "45.0.0", "helmet": "^3.21.2", "http-proxy": "^1.17.0", + "jsonwebtoken": "^8.5.1", + "jwt-decode": "^2.2.0", "keycloak-admin": "^1.13.0", "keycloak-connect": "git+https://github.com/sunbird-cb/keycloak-nodejs-connect.git#release-2.5.0", "lodash": "^4.17.21", diff --git a/src/configs/keycloak.config.ts b/src/configs/keycloak.config.ts index 7bd50d64..030c54b2 100644 --- a/src/configs/keycloak.config.ts +++ b/src/configs/keycloak.config.ts @@ -11,3 +11,15 @@ export function getKeycloakConfig(url?: string, realm?: string) { 'confidential-port': 0, } } + +export function getOAuthKeycloakConfig() { + return { + bearerOnly: true, + credentials: { + secret: CONSTANTS.KEYCLOAK_GOOGLE_CLIENT_SECRET, + }, + realm: CONSTANTS.PORTAL_REALM, + resource: CONSTANTS.KEYCLOAK_GOOGLE_CLIENT_ID, + serverUrl: CONSTANTS.PORTAL_AUTH_SERVER_URL, + } +} diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 8949da07..a546015a 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -5,7 +5,7 @@ import FormData from 'form-data' import lodash from 'lodash' import { axiosRequestConfig } from '../configs/request.config' import { CONSTANTS } from '../utils/env' -import { logInfo } from '../utils/logger' +import { logError, logInfo } from '../utils/logger' import { ilpProxyCreatorRoute, // proxyCreatorDiscussion, @@ -200,9 +200,47 @@ proxiesV8.use('/read/content-progres/*', proxyCreatorSunbirdSearch(express.Router(), `${CONSTANTS.KONG_API_BASE}/course/v1/content/state/read`) ) -proxiesV8.use('/api/user/v2/read', - proxyCreatorToAppentUserId(express.Router(), `${CONSTANTS.KONG_API_BASE}/user/v2/read/`) -) +proxiesV8.get('/api/user/v2/read', async (req, res) => { + const host = req.get('host') + const originalUrl = req.originalUrl + const lastIndex = originalUrl.lastIndexOf('/') + const subStr = originalUrl.substr(lastIndex).substr(1).split('-').length + let userId = extractUserIdFromRequest(req).split(':')[2] + if (subStr === 5 && (originalUrl.substr(lastIndex).substr(1))) { + userId = originalUrl.substr(lastIndex).substr(1) + } + + await axios({ + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-token': extractUserToken(req), + }, + method: 'GET', + url: `${CONSTANTS.KONG_API_BASE}/user/v2/read/` + userId, + }).then((response) => { + if (response.data.responseCode === 'OK') { + res.status(200).send(response.data) + } else { + logError('User Read API.. Received non OK response.' + JSON.stringify(response.data)) + res.redirect(`https://${host}/public/logout?error=` + encodeURIComponent(JSON.stringify(response.data.params.errmsg))) + } + }).catch((err) => { + let errMsg = 'Internal Server Error' + if (err.response && err.response.data) { + logError('Received error for user read API. Error: ' + JSON.stringify(err.response.data)) + errMsg = err.response.data.params.errmsg + } + if (req.session) { + req.session.destroy((dErr) => { + logError('Failed to clear the session. ERROR: ' + JSON.stringify(dErr)) + }) + } + res.clearCookie('connect.sid', { path: '/' }) + res.redirect(`https://${host}/public/logout?error=` + encodeURIComponent(errMsg)) + }) +}) proxiesV8.use('/api/user/v5/read', proxyCreatorToAppentUserId(express.Router(), `${CONSTANTS.KONG_API_BASE}/user/v5/read/`) @@ -293,6 +331,11 @@ proxiesV8.use('/storage/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) +proxiesV8.use('/forms/*', + // tslint:disable-next-line: max-line-length + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + // proxiesV8.use('/api/framework/*', // // tslint:disable-next-line: max-line-length // proxyCreatorQML(express.Router(), `${CONSTANTS.KONG_API_BASE}`, '/api/') diff --git a/src/publicApi_v8/googleOAuthHelper.ts b/src/publicApi_v8/googleOAuthHelper.ts new file mode 100644 index 00000000..c508936c --- /dev/null +++ b/src/publicApi_v8/googleOAuthHelper.ts @@ -0,0 +1,87 @@ +const { google } = require('googleapis') +import { CONSTANTS } from '../utils/env' +import { logError, logInfo } from '../utils/logger' +import { getKeyCloakClient } from './keycloakHelper' + +const redirectPath = '/apis/public/v8/google/callback' +const defaultScope = ['https://www.googleapis.com/auth/userinfo.email+https://www.googleapis.com/auth/userinfo.profile+openid'] + +// tslint:disable-next-line: no-any +export function createConnection(req: any) { + const redirect = `https://${req.get('host')}${redirectPath}` + return new google.auth.OAuth2(CONSTANTS.GOOGLE_CLIENT_ID, CONSTANTS.GOOGLE_CLIENT_SECRET, redirect) +} + +// tslint:disable-next-line: no-any +export function generateAuthUrl(req: any) { + const connection = createConnection(req) + return connection.generateAuthUrl({ + access_type: 'offline', + prompt: 'consent', + scope: defaultScope, + }) +} + +// tslint:disable-next-line: no-any +export async function getGoogleProfile(req: any) { + try { + const client = createConnection(req) + if (req.query.error === 'access_denied') { + throw new Error('GOOGLE_ACCESS_DENIED') + } + const { tokens } = await client.getToken(req.query.code) + client.setCredentials(tokens) + logInfo('userInformation being fetched from oauth2 api') + const oauth2 = await google.oauth2({ + auth: client, + version: 'v2', + }) + const googleProfileFetched = await oauth2.userinfo.get() || {} + logInfo('userInformation fetched -> ' + JSON.stringify(googleProfileFetched.data)) + return { + emailId: googleProfileFetched.data.email, + firstName: googleProfileFetched.data.given_name, + lastName: googleProfileFetched.data.family_name, + name: googleProfileFetched.data.name, + } + } catch (err) { + logError('Failed to get user profile: ' + JSON.stringify(err)) + } + return {} +} + +// tslint:disable-next-line: no-any +export async function getQueryParams(queryObj: any) { + return '?' + Object.keys(queryObj) + .map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(queryObj[key])}`) + .join('&') +} + +// tslint:disable-next-line: no-any +export async function createSession(emailId: string, req: any, res: any) { + // tslint:disable-next-line: no-any + let grant: { access_token: { token: any }; refresh_token: { token: any } } + const scope = 'offline_access' + const keycloakClient = getKeyCloakClient() + logInfo('login in progress') + // tslint:disable-next-line: no-any + try { + grant = await keycloakClient.grantManager.obtainDirectly(emailId, undefined, undefined, scope) + } catch (err) { + logError('googleOauthHelper: createSession failed') + throw new Error('unable to create session') + } + keycloakClient.storeGrant(grant, req, res) + req.kauth.grant = grant + return new Promise((resolve, reject) => { + // tslint:disable-next-line: no-any + keycloakClient.authenticated(req, (error: any) => { + if (error) { + logError('googleauthhelper:createSession error creating session') + reject('GOOGLE_CREATE_SESSION_FAILED') + } else { + resolve({access_token: grant.access_token.token, refresh_token: grant.refresh_token.token}) + } + }) + }) +} diff --git a/src/publicApi_v8/googleauth.ts b/src/publicApi_v8/googleauth.ts new file mode 100644 index 00000000..0f48c183 --- /dev/null +++ b/src/publicApi_v8/googleauth.ts @@ -0,0 +1,77 @@ +import express from 'express' +import { CONSTANTS } from '../utils/env' +import { logError, logInfo } from '../utils/logger' +import { getGoogleProfile } from './googleOAuthHelper' +import { createUserWithMailId, fetchUserByEmailId, updateKeycloakSession } from './ssoUserHelper' + +export const googleAuth = express.Router() + +const googleScope = 'https://www.googleapis.com/auth/userinfo.email%20https://www.googleapis.com/auth/userinfo.profile' +const otherParams = '&prompt=consent&response_type=code&scope=' +const googleUrl = 'https://accounts.google.com/o/oauth2/v2/auth?' +const HTTPS_PROTO = 'https://' +const clientIdWithValue = 'client_id=' + CONSTANTS.GOOGLE_CLIENT_ID +const redirectUri = '&redirect_uri=' + +googleAuth.get(['/auth', '/authV2', '/testauth'], async (req, res) => { + let redirectUrlHost = HTTPS_PROTO + req.hostname + if (req.url.substring(req.url.lastIndexOf('/')) === '/auth') { + redirectUrlHost = redirectUrlHost + CONSTANTS.GOOGLE_AUTH_CALLBACK_URL + } else if (req.url.substring(req.url.lastIndexOf('/')) === '/authV2') { + redirectUrlHost = redirectUrlHost + '/public/google/sso' + } else if (req.url.substring(req.url.lastIndexOf('/')) === '/testauth') { + redirectUrlHost = redirectUrlHost + '/apis/public/v8/google/callback' + } + let oAuthParams = clientIdWithValue + redirectUri + redirectUrlHost + oAuthParams = oAuthParams + otherParams + googleScope + res.redirect(googleUrl + oAuthParams) +}) + +googleAuth.get('/callback', async (req, res) => { + const host = req.get('host') + let resRedirectUrl = `https://${host}/page/home` + try { + logInfo('Successfully received callback from google. Received query params -> ' + JSON.stringify(req.query)) + const googleProfile = await getGoogleProfile(req) + logInfo('Successfully got authenticated with google...') + logInfo('Email: ' + googleProfile.emailId) + let result: { errMessage: string, userExist: boolean, } + result = await fetchUserByEmailId(googleProfile.emailId) + logInfo('isUserExist ? ' + result.userExist + ', errorMessage ? ' + result.errMessage) + let isFirstTimeUser = false + if (result.errMessage === '') { + let createResult: { errMessage: string, userCreated: boolean, userId: string } + if (!result.userExist) { + createResult = await createUserWithMailId(googleProfile.emailId, + googleProfile.firstName, googleProfile.lastName) + if (createResult.errMessage !== '') { + result.errMessage = createResult.errMessage + } + isFirstTimeUser = true + } + + if (result.errMessage === '') { + let keycloakResult: { + access_token: string, errMessage: string, keycloakSessionCreated: boolean, refresh_token: string + } + keycloakResult = await updateKeycloakSession(googleProfile.emailId, req, res) + logInfo('Keycloak Session Details:: ' + JSON.stringify(keycloakResult)) + if (keycloakResult.errMessage !== '') { + result.errMessage = keycloakResult.errMessage + } else { + isFirstTimeUser = true + } + } + } + if (result.errMessage !== '') { + logInfo('Received error in processing... Error ' + result.errMessage) + resRedirectUrl = `https://${host}/public/logout?error=` + encodeURIComponent(JSON.stringify(result.errMessage)) + } else if (isFirstTimeUser) { + resRedirectUrl = `https://${host}/public/welcome` + } + } catch (err) { + logError('Failed to process callback event. Error: ' + JSON.stringify(err)) + resRedirectUrl = `https://${host}/public/logout?error=` + encodeURIComponent(JSON.stringify(err)) + } + res.redirect(resRedirectUrl) +}) diff --git a/src/publicApi_v8/keycloakHelper.ts b/src/publicApi_v8/keycloakHelper.ts new file mode 100644 index 00000000..731cefc9 --- /dev/null +++ b/src/publicApi_v8/keycloakHelper.ts @@ -0,0 +1,77 @@ +const keycloak = require('keycloak-connect') +const async = require('async') +import request from 'request' +import { getOAuthKeycloakConfig } from '../configs/keycloak.config' +import { getSessionConfig } from '../configs/session.config' +import { CONSTANTS } from '../utils/env' +import { logError, logInfo } from '../utils/logger' +import { PERMISSION_HELPER } from '../utils/permissionHelper' + +export function getKeyCloakClient() { + const sessionConfig = getSessionConfig() + const keycloakObj = new keycloak({ store: sessionConfig.store }, getOAuthKeycloakConfig()) + keycloakObj.authenticated = authenticated + keycloakObj.deauthenticated = deauthenticated + return keycloakObj +} + +// tslint:disable-next-line: no-any +const deauthenticated = async (reqObj: any) => { + logInfo('keycloakHelper::deauthenticated...') + const keyCloakPropertyName = 'keycloak-token' + if (reqObj.session.hasOwnProperty(keyCloakPropertyName)) { + const keycloakToken = reqObj.session[keyCloakPropertyName] + if (keycloakToken) { + const tokenObject = JSON.parse(keycloakToken) + const refreshToken = tokenObject.refresh_token + if (refreshToken) { + const host = reqObj.get('host') + const urlValue = `https://${host}` + '/auth/realms/' + CONSTANTS.KEYCLOAK_REALM + '/protocol/openid-connect/logout' + try { + request.post({ + form: { + client_id: CONSTANTS.KEYCLOAK_GOOGLE_CLIENT_ID, + client_secret: CONSTANTS.KEYCLOAK_GOOGLE_CLIENT_SECRET, + refresh_token: refreshToken, + }, + url: urlValue, + }) + } catch (err) { + // tslint:disable-next-line: no-console + console.log('Failed to call keycloak logout API ', err, '------', new Date().toString()) + } + } else { + logError('Not able to retrieve refresh_token value from Session. Logout process failed.') + } + } else { + logError('Not able to retrieve keycloak-token value from Session. Logout process failed.') + } + } else { + logError('Session does not have property with name: ' + keyCloakPropertyName) + } + delete reqObj.session.userRoles + delete reqObj.session.userId + reqObj.session.destroy() + logInfo(`${process.pid}: User Deauthenticated`) +} + +// tslint:disable-next-line: no-any +const authenticated = async (reqObj: any, next: any) => { + logInfo('keycloakHelper::authenticated...') + const postLoginRequest = [] + // tslint:disable-next-line: no-any + postLoginRequest.push((callback: any) => { + PERMISSION_HELPER.getCurrentUserRoles(reqObj, callback) + }) + + // tslint:disable-next-line: no-any + async.series(postLoginRequest, (err: any) => { + if (err) { + logError('error loggin in user', '------', new Date().toString()) + next(err, null) + } else { + logInfo(`${process.pid}: User authenticated`, '------', new Date().toString()) + next(null, 'loggedin') + } + }) +} diff --git a/src/publicApi_v8/parichayAuth.ts b/src/publicApi_v8/parichayAuth.ts new file mode 100644 index 00000000..15c024c5 --- /dev/null +++ b/src/publicApi_v8/parichayAuth.ts @@ -0,0 +1,99 @@ +import axios from 'axios' +import express from 'express' +import { axiosRequestConfig } from '../configs/request.config' +import { CONSTANTS } from '../utils/env' +import { getCurrnetExpiryTime } from '../utils/jwtHelper' +import { logError, logInfo } from '../utils/logger' +import { createUserWithMailId, fetchUserByEmailId, updateKeycloakSession } from './ssoUserHelper' + +export const parichayAuth = express.Router() + +parichayAuth.get('/auth', async (req, res) => { + logInfo('Received host : ' + req.hostname) + const redirectUrl = 'https://' + req.hostname + CONSTANTS.PARICHAY_AUTH_CALLBACK_URL + let oAuthParams = 'client_id=' + CONSTANTS.PARICHAY_CLIENT_ID + oAuthParams = oAuthParams + '&redirect_uri=' + redirectUrl + oAuthParams = oAuthParams + '&response_type=code&scope=user_details' + oAuthParams = oAuthParams + '&code_challenge=' + CONSTANTS.PARICHAY_CODE_CHALLENGE + oAuthParams = oAuthParams + '&code_challenge_method=S256' + const parichayUrl = CONSTANTS.PARICHAY_AUTH_URL + '?' + oAuthParams + res.redirect(parichayUrl) +}) + +parichayAuth.get('/callback', async (req, res) => { + const host = req.get('host') + if (!req.query.code) { + res.redirect(`https://${host}/public/logout?error=` + encodeURIComponent('Failed to login in Parichay. Code param is missing.')) + return + } + let resRedirectUrl = `https://${host}/page/home` + try { + const redirectUrl = 'https://' + req.hostname + CONSTANTS.PARICHAY_AUTH_CALLBACK_URL + const tokenResponse = await axios({ + ...axiosRequestConfig, + data: { + client_id: CONSTANTS.PARICHAY_CLIENT_ID, + client_secret: CONSTANTS.PARICHAY_CLIENT_SECRET, + code: decodeURIComponent(req.query.code), + // tslint:disable-next-line: max-line-length + code_verifier: CONSTANTS.PARICHAY_CODE_VERIFIER, + grant_type: 'authorization_code', + redirect_uri: redirectUrl, + }, + method: 'POST', + url: CONSTANTS.PARICHAY_TOKEN_URL, + }) + if (req.session) { + req.session.parichayToken = tokenResponse.data + req.session.cookie.expires = new Date(getCurrnetExpiryTime(tokenResponse.data.access_token)) + logInfo('Parichay Token is set in request Session.') + } else { + logError('Failed to set parichay token in req session. Session not available...') + } + const userDetailResponse = await axios({ + ...axiosRequestConfig, + headers: { + Authorization: tokenResponse.data.access_token, + }, + method: 'GET', + url: CONSTANTS.PARICHAY_USER_DETAILS_URL, + }) + + logInfo('User information from Parichay : ' + JSON.stringify(userDetailResponse.data)) + let isFirstTimeUser = false + let result: { errMessage: string, userExist: boolean, } + result = await fetchUserByEmailId(userDetailResponse.data.loginId) + if (result.errMessage === '') { + let createResult: { errMessage: string, userCreated: boolean, userId: string } + if (!result.userExist) { + logInfo('Sunbird User does not exist for email: ' + userDetailResponse.data.loginId) + createResult = await createUserWithMailId(userDetailResponse.data.loginId, + userDetailResponse.data.FirstName, userDetailResponse.data.LastName) + if (createResult.errMessage !== '') { + result.errMessage = createResult.errMessage + } + } + if (result.errMessage === '') { + let keycloakResult: { + access_token: string, errMessage: string, keycloakSessionCreated: boolean, refresh_token: string + } + keycloakResult = await updateKeycloakSession(userDetailResponse.data.loginId, req, res) + if (keycloakResult.errMessage !== '') { + result.errMessage = keycloakResult.errMessage + } else { + isFirstTimeUser = true + } + } + } + if (result.errMessage !== '') { + logInfo('Received error from user search. ') + resRedirectUrl = `https://${host}/public/logout?error=` + encodeURIComponent(JSON.stringify(result.errMessage)) + } else if (isFirstTimeUser) { + resRedirectUrl = `https://${host}/public/welcome` + } + } catch (err) { + logError('Failed to process callback API.. error: ' + JSON.stringify(err)) + resRedirectUrl = `https://${host}/public/logout?error=` + encodeURIComponent('Internal Server Error. Please contact administrator.') + } + res.redirect(resRedirectUrl) +}) diff --git a/src/publicApi_v8/ssoUserHelper.ts b/src/publicApi_v8/ssoUserHelper.ts new file mode 100644 index 00000000..b754daf6 --- /dev/null +++ b/src/publicApi_v8/ssoUserHelper.ts @@ -0,0 +1,180 @@ +import axios from 'axios' +import { axiosRequestConfig } from '../configs/request.config' +import { CONSTANTS } from '../utils/env' +import { logError, logInfo } from '../utils/logger' +import { getKeyCloakClient } from './keycloakHelper' + +const API_END_POINTS = { + kongAssignRoleUser: `${CONSTANTS.KONG_API_BASE}/user/private/v1/assign/role`, + kongSignUpUser: `${CONSTANTS.KONG_API_BASE}/user/v2/signup`, + kongUpdateUser: `${CONSTANTS.KONG_API_BASE}/user/private/v1/update`, + kongUserRead: (userId: string) => `${CONSTANTS.KONG_API_BASE}/user/private/v1/read/${userId}`, +} + +export async function fetchUserByEmailId(emailId: string) { + const sbUserSearchRes = await axios({ + ...axiosRequestConfig, + data: { request: { + fields : ['userId', 'status', 'channel', 'rootOrgId', 'organisations'], + filters: { email: emailId.toLowerCase() }, + } }, + method: 'POST', + url: CONSTANTS.LEARNER_SERVICE_API_BASE + '/private/user/v1/search', + }) + const result = { + errMessage : '', userExist : false, + } + + if (sbUserSearchRes.data.responseCode.toUpperCase() === 'OK') { + if (sbUserSearchRes.data.result.response.count === 0) { + logInfo('user accound doesnot exist. returning false') + } else if (sbUserSearchRes.data.result.response.count === 1) { + const contentObj = sbUserSearchRes.data.result.response.content[0] + const status = contentObj.status + logInfo('user account exist. Data: ' + JSON.stringify(sbUserSearchRes.data) + ', Status: ' + status) + if (status === 1) { + logInfo('user account enabled. returning true') + result.userExist = true + } else { + logInfo('user account is diabled. throwing error') + result.errMessage = 'Account Disabled. Please contact Admin.' + } + } else { + result.errMessage = 'More than one user account exists. Please contact Admin.' + } + } else { + logError('googleOauthHelper: fetchUserByEmailId failed' + JSON.stringify(sbUserSearchRes.data)) + result.errMessage = 'Failed to verify email exist. Internal Server Error.' + } + return Promise.resolve(result) +} + +export async function createUserWithMailId(emailId: string, firstNameStr: string, lastNameStr: string) { + const result = { + errMessage : '', userCreated : false, userId: '', + } + const signUpErr = 'SIGN_UP_ERR-' + let statusString = '' + const signUpResponse = await axios({ + ...axiosRequestConfig, + data: { + request: + { + email: emailId, + emailVerified: true, + firstName: firstNameStr, + lastName : lastNameStr, + } }, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + }, + method: 'POST', + url: API_END_POINTS.kongSignUpUser, + }) + statusString = signUpResponse.data.params.status + if (statusString.toUpperCase() !== 'SUCCESS') { + result.errMessage = signUpErr + 'FAILED_TO_CREATE_USER' + return Promise.resolve(result) + } + result.userCreated = true + result.userId = signUpResponse.data.result.userId + const sbUserReadResponse = await axios({ + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + }, + method: 'GET', + url: API_END_POINTS.kongUserRead(result.userId), + }) + statusString = sbUserReadResponse.data.params.status + if (statusString.toUpperCase() !== 'SUCCESS') { + result.errMessage = signUpErr + 'FAILED_TO_READ_CREATED_USER' + return Promise.resolve(result) + } + const sbUserOrgId = sbUserReadResponse.data.result.response.rootOrgId + const sbProfileUpdateReq = { + profileDetails: { + employmentDetails: { + departmentName: sbUserReadResponse.data.result.response.channel, + }, + personalDetails: { + firstname: firstNameStr, + primaryEmail: emailId, + surname: lastNameStr, + }, + }, + userId: result.userId, + } + const sbUserProfileUpdateResp = await axios({ + ...axiosRequestConfig, + data: { request: sbProfileUpdateReq }, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + }, + method: 'PATCH', + url: API_END_POINTS.kongUpdateUser, + }) + statusString = sbUserProfileUpdateResp.data.params.status + if (statusString.toUpperCase() !== 'SUCCESS') { + result.errMessage = signUpErr + 'FAILED_TO_UPDATE_USER' + return Promise.resolve(result) + } + const sbAssignRoleResp = await axios({ + ...axiosRequestConfig, + data: { + request: { + organisationId: sbUserOrgId, + roles: ['PUBLIC'], + userId: result.userId, + }, + }, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + }, + method: 'POST', + url: API_END_POINTS.kongAssignRoleUser, + }) + statusString = sbAssignRoleResp.data.params.status + if (statusString.toUpperCase() !== 'SUCCESS') { + result.errMessage = signUpErr + 'FAILED_TO_UPDATE_USER' + return Promise.resolve(result) + } + return Promise.resolve(result) +} + +// tslint:disable-next-line: no-any +export async function updateKeycloakSession(emailId: string, req: any, res: any) { + const scope = 'offline_access' + const keycloakClient = getKeyCloakClient() + // tslint:disable-next-line: no-any + let grant: { access_token: { token: any }; refresh_token: { token: any } } + const result = { + access_token: '', errMessage : '', keycloakSessionCreated: false, refresh_token: '', + } + try { + grant = await keycloakClient.grantManager.obtainDirectly(emailId, undefined, undefined, scope) + keycloakClient.storeGrant(grant, req, res) + req.kauth.grant = grant + const userId = req.kauth.grant.access_token.content.sub.split(':') + req.session.userId = userId[userId.length - 1] + logInfo('userId ::', userId, '------', new Date().toString()) + req.session.keycloakClientId = CONSTANTS.KEYCLOAK_GOOGLE_CLIENT_ID + req.session.keycloakClientSecret = CONSTANTS.KEYCLOAK_GOOGLE_CLIENT_SECRET + result.access_token = grant.access_token.token + result.refresh_token = grant.refresh_token.token + result.keycloakSessionCreated = true + // tslint:disable-next-line: no-any + keycloakClient.authenticated(req, (error: any) => { + logInfo('ssoUserHelper::keycloakClient::authenticated..') + if (error) { + logError('googleauthhelper:createSession error creating session') + result.errMessage = 'GOOGLE_CREATE_SESSION_FAILED' + } + }) + return Promise.resolve(result) + } catch (err) { + logError('googleOauthHelper: createSession failed. Error: ' + JSON.stringify(err)) + result.errMessage = 'FAILED_TO_CREATE_KEYCLOAK_SESSION' + } + return Promise.resolve(result) +} diff --git a/src/utils/custom-keycloak.ts b/src/utils/custom-keycloak.ts index 610d5e34..146b1a33 100644 --- a/src/utils/custom-keycloak.ts +++ b/src/utils/custom-keycloak.ts @@ -93,18 +93,40 @@ export class CustomKeycloak { if (refreshToken) { const host = reqObj.get('host') const urlValue = `https://${host}` + '/auth/realms/' + CONSTANTS.KEYCLOAK_REALM + '/protocol/openid-connect/logout' + const formData: Record = { + client_id: 'portal', + refresh_token: refreshToken, + } + + if (reqObj.session.hasOwnProperty('keycloakClientId') && (reqObj.session.keycloakClientId !== '')) { + formData.client_id = reqObj.session.keycloakClientId + formData.client_secret = reqObj.session.keycloakClientSecret + } + logInfo('formData used in logout: ' + JSON.stringify(formData)) try { request.post({ - form: { - client_id: 'portal', - refresh_token: refreshToken, - }, + form: formData, url: urlValue, }) } catch (err) { // tslint:disable-next-line: no-console console.log('Failed to call keycloak logout API ', err, '------', new Date().toString()) } + + if (reqObj.session.parichayToken) { + logInfo('Parichay login found... trying to logout from Parichay...') + try { + request.get({ + headers: { + Authorization: reqObj.session.parichayToken.access_token, + }, + url: CONSTANTS.PARICHAY_REVOKE_URL, + }) + } catch (err) { + // tslint:disable-next-line: no-console + console.log('Failed to call parichay revoke API ', err, '------', new Date().toString()) + } + } } else { logError('Not able to retrieve refresh_token value from Session. Logout process failed.') } @@ -116,6 +138,8 @@ export class CustomKeycloak { } delete reqObj.session.userRoles delete reqObj.session.userId + delete reqObj.session.keycloakClientId + delete reqObj.session.keycloakClientSecret reqObj.session.destroy() logInfo(`${process.pid}: User Deauthenticated`) } diff --git a/src/utils/env.ts b/src/utils/env.ts index 1d86cce0..b26d23b5 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -1,3 +1,4 @@ + const env = process.env const HTTPS_HOST = env.HTTPS_HOST || 'https://igot-dev.in' export const CONSTANTS = { @@ -160,6 +161,24 @@ export const CONSTANTS = { // tslint:disable-next-line: max-line-length KEYCLOAK_PUBLIC_KEY: 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoBa+pmsxpPhGED2/zexM/PL+rLv2YnLCbX2agY2fllucCvnPNmWRTyg8L+qgkJBpHuhcHqXEFTfBm2WCe4yke+0LnGi6SGsCJTrc2Sh+Lgo/L9r1lr7uOoD8BHE6ywnbHG2ok7qyomCTD9LpG1WXFk4T3WUH9+gN2vm77s3YJz2yfo2UlMd1Dp9nWZhfwB1vwJLuRHnqEKkSH8M2K8SFRdpgOyLdM9fprr7Ebgr7SWdoAy2q3mUONSmDsftKDeEWjr6MXGTATfbMfKE3GFqddMjFHkmvh42aM+RlU3N2xo2msF8M2sYF6YlIDkED+9h+y0QROJjQlBr774GAd06I0wIDAQAB', KARMAYOGI_PORTAL_HOST: env.KARMAYOGI_PORTAL_HOST || 'igot-dev.in', + GOOGLE_CLIENT_ID: env.GOOGLE_CLIENT_ID || 'googleClientId', + GOOGLE_CLIENT_SECRET: env.GOOGLE_CLIENT_SECRET || 'googleClientSecret', + PARICHAY_CLIENT_ID: env.PARICHAY_CLIENT_ID || 'parichayClientId', + PARICHAY_CLIENT_SECRET: env.PARICHAY_CLIENT_SECRET || 'parichayClientSecret', + KEYCLOAK_GOOGLE_CLIENT_ID: env.KEYCLOAK_GOOGLE_CLIENT_ID || 'keycloakGoogleClientId', + KEYCLOAK_GOOGLE_CLIENT_SECRET: env.KEYCLOAK_GOOGLE_CLIENT_SECRET || 'keycloakGoogleClientSecret', + PORTAL_AUTH_SERVER_URL: env.PORTAL_AUTH_SERVER_URL || 'https://igot-dev.in/auth', + PARICHAY_CODE_CHALLENGE: env.PARICHAY_CODE_CHALLENGE || 'parichayCodeChallenge', + PARICHAY_CODE_VERIFIER: env.PARICHAY_CODE_VERIFIER || 'parichayCodeVerfifier', + PARICHAY_AUTH_URL: env.PARICHAY_AUTH_URL || 'parichayAuthUrl', + PARICHAY_AUTH_CALLBACK_URL: env.PARICHAY_AUTH_CALLBACK_URL || '/apis/public/v8/parichay/callback', + PARICHAY_TOKEN_URL: env.PARICHAY_TOKEN_URL || 'parichayTokenUrl', + PARICHAY_USER_DETAILS_URL: env.PARICHAY_USER_DETAILS_URL || 'parichayUserDetailsUrl', + GOOGLE_AUTH_CALLBACK_URL: env.GOOGLE_AUTH_CALLBACK_URL || '/apis/public/v8/google/callback', + CUSTODIAN_ORG_ID: env.CUSTODIAN_ORG_ID || 'id', + CUSTODIAN_ORG_CHANNEL: env.CUSTODIAN_ORG_CHANNEL || 'igot', + PARICHAY_REVOKE_URL: env.PARICHAY_REVOKE_URL || 'parichayRevokeUrl', + } export const RESTRICTED_PYTHON_STMT: string[] = process.env.RESTRICTED_CHARACTERS diff --git a/src/utils/jwtHelper.ts b/src/utils/jwtHelper.ts new file mode 100644 index 00000000..5182bf1b --- /dev/null +++ b/src/utils/jwtHelper.ts @@ -0,0 +1,33 @@ +import { CONSTANTS } from './env' + +const jwt = require('jsonwebtoken') + +// tslint:disable-next-line: no-any +export function getExpiryTime(accessToken: any) { + const jwtPayload = jwt.decode(accessToken) + if (jwtPayload && jwtPayload.exp) { + const diff = ((Math.floor(Date.now() / 1000)) - jwtPayload.exp) + if (diff > 0) { + return diff + } else { + return 0 + } + } + return 0 +} + +// tslint:disable-next-line: no-any +export function getCurrnetExpiryTime(accessToken: any) { + const jwtPayload = jwt.decode(accessToken) + if (jwtPayload && jwtPayload.exp) { + return jwtPayload.exp * 1000 + } else { + // return configured value for KEYCLOAK + return CONSTANTS.KEYCLOAK_SESSION_TTL + } +} + +// tslint:disable-next-line: no-any +export function decodeCode(accessToken: any) { + return jwt.decode(accessToken) +} diff --git a/src/utils/permissionHelper.ts b/src/utils/permissionHelper.ts index 2982261f..657b17b3 100644 --- a/src/utils/permissionHelper.ts +++ b/src/utils/permissionHelper.ts @@ -21,6 +21,13 @@ export const PERMISSION_HELPER = { reqObj.session.userRoles = userData.result.response.roles reqObj.session.orgs = userData.result.response.organisations reqObj.session.rootOrgId = userData.result.response.rootOrgId + reqObj.session.channel = userData.result.response.channel + if (userData.result.response.hasOwnProperty('profileDetails') && + userData.result.response.profileDetails.hasOwnProperty('userRoles')) { + reqObj.session.userPositions = userData.result.response.profileDetails.userRoles + } else { + reqObj.session.userPositions = [] + } if (!_.includes(reqObj.session.userRoles, 'PUBLIC')) { reqObj.session.userRoles.push('PUBLIC') } @@ -57,7 +64,7 @@ export const PERMISSION_HELPER = { callback(error, null) } else { // tslint:disable-next-line: no-console - console.log(`setNodeBBUID::Success of save -- reqObj.session ${new Date()}--- `, reqObj.session ) + console.log(`setNodeBBUID::Success of save -- reqObj.session ${new Date()}--- `) callback(null, nodeBBData) } }) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 7b9720dc..01fba08b 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -159,9 +159,6 @@ export function proxyCreatorSunbird(route: Router, targetUrl: string, _timeout = // tslint:disable-next-line: no-console console.log('REQ_URL_ORIGINAL proxyCreatorSunbird ======= discussion', url) } - - // tslint:disable-next-line: no-console - console.log('REQ_SESSION proxyCreatorSunbird : ', req.session) if (req.originalUrl.includes('/dashboard') && !req.originalUrl.includes('/dashboard/analytics/getChartV2/Karmayogi') && req.session) { if (req.originalUrl.includes('?')) { diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 01f4c904..3fe2c344 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -304,6 +304,34 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/forms/createForm': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/forms/getFormById': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/forms/getAllForms': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/forms/v1/saveFormSubmit': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/upload/action/content/v3/upload/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1797,6 +1825,20 @@ export const API_LIST = { ROLE.STATE_ADMIN, ], }, + '/proxies/v8/user/basicInfo': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/user/basicProfileUpdate': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -1884,6 +1926,10 @@ export const API_LIST = { '/proxies/v8/dashboard/analytics/getDashboardsForProfile/Karmayogi', '/proxies/v8/dashboard/analytics/getChartV2/Karmayogi', '/proxies/v8/wat/dashboard/getDashboardConfig/Karmayogi/overview', + '/proxies/v8/forms/createForm', + '/proxies/v8/forms/getFormById', + '/proxies/v8/forms/getAllForms', + '/proxies/v8/forms/v1/saveFormSubmit', '/protected/v8/user/profileDetails/test', '/protected/v8/resource/', '/protected/v8/user/details', @@ -2035,5 +2081,7 @@ export const API_LIST = { '/proxies/v8/ratings/v1/ratingLookUp', '/proxies/v8/user/v1/positions', '/proxies/v8/org/ext/v1/create', + '/proxies/v8/user/basicInfo', + '/proxies/v8/user/basicProfileUpdate', ], } From 4361af5008a40d9b9328d50545c2fb55290196cd Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Sat, 30 Jul 2022 14:53:01 +0530 Subject: [PATCH 346/856] Fix for network user read API --- src/proxies_v8/proxies_v8.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index a546015a..91d542e0 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -200,7 +200,7 @@ proxiesV8.use('/read/content-progres/*', proxyCreatorSunbirdSearch(express.Router(), `${CONSTANTS.KONG_API_BASE}/course/v1/content/state/read`) ) -proxiesV8.get('/api/user/v2/read', async (req, res) => { +proxiesV8.get(['/api/user/v2/read', '/api/user/v2/read/:id'], async (req, res) => { const host = req.get('host') const originalUrl = req.originalUrl const lastIndex = originalUrl.lastIndexOf('/') From 5a0c955a9ec26079da12647f95fb704d4ddd2801 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Sat, 30 Jul 2022 14:53:53 +0530 Subject: [PATCH 347/856] Added route for Parichay --- src/publicApi_v8/publicApiV8.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/publicApi_v8/publicApiV8.ts b/src/publicApi_v8/publicApiV8.ts index b62d400e..2178d469 100644 --- a/src/publicApi_v8/publicApiV8.ts +++ b/src/publicApi_v8/publicApiV8.ts @@ -1,6 +1,7 @@ import express from 'express' import { CONSTANTS } from '../utils/env' import { proxyCreatorRoute } from '../utils/proxyCreator' +import { parichayAuth } from './parichayAuth' import { workallocationPublic } from './workallocationPublic' export const publicApiV8 = express.Router() @@ -17,3 +18,5 @@ publicApiV8.use('/assets', publicApiV8.use('/workallocation', workallocationPublic) publicApiV8.use('/org/v1/list', proxyCreatorRoute(express.Router(), CONSTANTS.KONG_API_BASE + '/org/v1/list')) + +publicApiV8.use('/parichay', parichayAuth) \ No newline at end of file From 7a2945af729223831dcbc7a9362406472fe2ce2c Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Fri, 23 Sep 2022 08:47:21 +0530 Subject: [PATCH 348/856] 4.8.0 bug fixes (#115) Handling redirect Url when user signs in Bulk upload APIs Optimized Parichay Login --- src/protectedApi_v8/resource.ts | 9 +++- src/proxies_v8/proxies_v8.ts | 60 ++++++++++++++++++++- src/publicApi_v8/googleauth.ts | 11 ++-- src/publicApi_v8/parichayAuth.ts | 15 ++++-- src/publicApi_v8/ssoUserHelper.ts | 71 ++----------------------- src/server.ts | 11 ++-- src/utils/custom-keycloak.ts | 13 +++++ src/utils/proxyCreator.ts | 5 ++ src/utils/whitelistApis.ts | 86 +++++++++++++++++++++++++++++-- 9 files changed, 193 insertions(+), 88 deletions(-) diff --git a/src/protectedApi_v8/resource.ts b/src/protectedApi_v8/resource.ts index 36d283e0..2c7cbd92 100644 --- a/src/protectedApi_v8/resource.ts +++ b/src/protectedApi_v8/resource.ts @@ -1,15 +1,22 @@ import { Router } from 'express' +import { logInfo } from '../utils/logger' const _ = require('lodash') export const userAuthKeyCloakApi = Router() userAuthKeyCloakApi.get('/', (req, res) => { const host = req.get('host') let queryParam = '' let isLocal = 0 + logInfo('Received query param: ' + req.query) if (!_.isEmpty(req.query)) { queryParam = req.query.q - if (queryParam.includes('localhost')) { + if (queryParam && queryParam.includes('localhost')) { isLocal = 1 } + if (req.query.redirect_uri) { + logInfo('Received redirectUrl value : ' + req.query.redirect_uri) + res.redirect(req.query.redirect_uri) + return + } } let redirectUrl = '' if (isLocal) { diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 91d542e0..d4cbfaac 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -32,6 +32,9 @@ const API_END_POINTS = { } export const proxiesV8 = express.Router() +const _ = require('lodash') + +const FILE_NOT_FOUND_ERR = 'File not found in the request' proxiesV8.get('/', (_req, res) => { res.json({ @@ -57,6 +60,7 @@ proxiesV8.post('/upload/*', (req, res) => { rootorg: 'igot', // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req), + // tslint:disable-next-line: all 'x-authenticated-userid': extractUserIdFromRequest(req), }, host: 'knowledge-mw-service', @@ -79,7 +83,7 @@ proxiesV8.post('/upload/*', (req, res) => { } ) } else { - res.send('File not found') + res.send(FILE_NOT_FOUND_ERR) } }) @@ -123,7 +127,7 @@ proxiesV8.post('/private/upload/*', (_req, _res) => { } ) } else { - _res.send('File not found') + _res.send(FILE_NOT_FOUND_ERR) } }) @@ -303,6 +307,53 @@ proxiesV8.use('/dashboard/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) +proxiesV8.post('/user/v1/bulkupload', (req, res) => { + if (req.files && req.files.data) { + const url = removePrefix('/proxies/v8', req.originalUrl) + const file: UploadedFile = req.files.data as UploadedFile + const formData = new FormData() + formData.append('file', Buffer.from(file.data), { + contentType: file.mimetype, + filename: file.name, + }) + let rootOrgId = _.get(req, 'session.rootOrgId') + if (!rootOrgId) { + rootOrgId = '' + } + formData.submit( + { + headers: { + // tslint:disable-next-line:max-line-length + Authorization: CONSTANTS.SB_API_KEY, + // tslint:disable-next-line: all + 'x-authenticated-user-orgid': rootOrgId, + 'x-authenticated-user-token': extractUserToken(req), + 'x-authenticated-userid': extractUserIdFromRequest(req), + }, + host: 'kong', + path: url, + port: 8000, + }, + // tslint:disable-next-line: all + (err, response) => { + // tslint:disable-next-line: all + response.on('data', (data) => { + if (!err && (response.statusCode === 200 || response.statusCode === 201)) { + res.send(JSON.parse(data.toString('utf8'))) + } else { + res.send(data.toString('utf8')) + } + }) + if (err) { + res.send(err) + } + } + ) + } else { + res.send(FILE_NOT_FOUND_ERR) + } +}) + proxiesV8.use('/user/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) @@ -429,6 +480,11 @@ proxiesV8.use('/ratings/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) +proxiesV8.use('/moderatoradmin/*', + // tslint:disable-next-line: max-line-length + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + function removePrefix(prefix: string, s: string) { return s.substr(prefix.length) } diff --git a/src/publicApi_v8/googleauth.ts b/src/publicApi_v8/googleauth.ts index 0f48c183..241ad8b6 100644 --- a/src/publicApi_v8/googleauth.ts +++ b/src/publicApi_v8/googleauth.ts @@ -35,9 +35,9 @@ googleAuth.get('/callback', async (req, res) => { const googleProfile = await getGoogleProfile(req) logInfo('Successfully got authenticated with google...') logInfo('Email: ' + googleProfile.emailId) - let result: { errMessage: string, userExist: boolean, } + let result: { errMessage: string, rootOrgId: string, userExist: boolean, } result = await fetchUserByEmailId(googleProfile.emailId) - logInfo('isUserExist ? ' + result.userExist + ', errorMessage ? ' + result.errMessage) + logInfo('isUserExist ? ' + result.userExist + 'rootOrgId: ? ' + result.rootOrgId + ', errorMessage ? ' + result.errMessage) let isFirstTimeUser = false if (result.errMessage === '') { let createResult: { errMessage: string, userCreated: boolean, userId: string } @@ -48,6 +48,11 @@ googleAuth.get('/callback', async (req, res) => { result.errMessage = createResult.errMessage } isFirstTimeUser = true + } else { + logInfo('result.rootOrgId = ' + result.rootOrgId + ', XChannelId = ' + CONSTANTS.X_Channel_Id) + if (result.rootOrgId !== '' && result.rootOrgId === CONSTANTS.X_Channel_Id) { + isFirstTimeUser = true + } } if (result.errMessage === '') { @@ -58,8 +63,6 @@ googleAuth.get('/callback', async (req, res) => { logInfo('Keycloak Session Details:: ' + JSON.stringify(keycloakResult)) if (keycloakResult.errMessage !== '') { result.errMessage = keycloakResult.errMessage - } else { - isFirstTimeUser = true } } } diff --git a/src/publicApi_v8/parichayAuth.ts b/src/publicApi_v8/parichayAuth.ts index 15c024c5..6dfe830f 100644 --- a/src/publicApi_v8/parichayAuth.ts +++ b/src/publicApi_v8/parichayAuth.ts @@ -46,7 +46,7 @@ parichayAuth.get('/callback', async (req, res) => { if (req.session) { req.session.parichayToken = tokenResponse.data req.session.cookie.expires = new Date(getCurrnetExpiryTime(tokenResponse.data.access_token)) - logInfo('Parichay Token is set in request Session.') + logInfo('Parichay Token is set in request Session.' + tokenResponse.data.access_token) } else { logError('Failed to set parichay token in req session. Session not available...') } @@ -60,9 +60,10 @@ parichayAuth.get('/callback', async (req, res) => { }) logInfo('User information from Parichay : ' + JSON.stringify(userDetailResponse.data)) - let isFirstTimeUser = false - let result: { errMessage: string, userExist: boolean, } + let result: { errMessage: string, rootOrgId: string, userExist: boolean, } result = await fetchUserByEmailId(userDetailResponse.data.loginId) + logInfo('isUserExist ? ' + result.userExist + 'rootOrgId: ? ' + result.rootOrgId + ', errorMessage ? ' + result.errMessage) + let isFirstTimeUser = false if (result.errMessage === '') { let createResult: { errMessage: string, userCreated: boolean, userId: string } if (!result.userExist) { @@ -72,6 +73,12 @@ parichayAuth.get('/callback', async (req, res) => { if (createResult.errMessage !== '') { result.errMessage = createResult.errMessage } + isFirstTimeUser = true + } else { + logInfo('result.rootOrgId = ' + result.rootOrgId + ', XChannelId = ' + CONSTANTS.X_Channel_Id) + if (result.rootOrgId !== '' && result.rootOrgId === CONSTANTS.X_Channel_Id) { + isFirstTimeUser = true + } } if (result.errMessage === '') { let keycloakResult: { @@ -80,8 +87,6 @@ parichayAuth.get('/callback', async (req, res) => { keycloakResult = await updateKeycloakSession(userDetailResponse.data.loginId, req, res) if (keycloakResult.errMessage !== '') { result.errMessage = keycloakResult.errMessage - } else { - isFirstTimeUser = true } } } diff --git a/src/publicApi_v8/ssoUserHelper.ts b/src/publicApi_v8/ssoUserHelper.ts index b754daf6..68694b2a 100644 --- a/src/publicApi_v8/ssoUserHelper.ts +++ b/src/publicApi_v8/ssoUserHelper.ts @@ -5,10 +5,7 @@ import { logError, logInfo } from '../utils/logger' import { getKeyCloakClient } from './keycloakHelper' const API_END_POINTS = { - kongAssignRoleUser: `${CONSTANTS.KONG_API_BASE}/user/private/v1/assign/role`, - kongSignUpUser: `${CONSTANTS.KONG_API_BASE}/user/v2/signup`, - kongUpdateUser: `${CONSTANTS.KONG_API_BASE}/user/private/v1/update`, - kongUserRead: (userId: string) => `${CONSTANTS.KONG_API_BASE}/user/private/v1/read/${userId}`, + cbExtSignUpUser: `${CONSTANTS.KONG_API_BASE}/user/v1/ext/signup`, } export async function fetchUserByEmailId(emailId: string) { @@ -22,7 +19,7 @@ export async function fetchUserByEmailId(emailId: string) { url: CONSTANTS.LEARNER_SERVICE_API_BASE + '/private/user/v1/search', }) const result = { - errMessage : '', userExist : false, + errMessage : '', rootOrgId: '', userExist : false, } if (sbUserSearchRes.data.responseCode.toUpperCase() === 'OK') { @@ -35,6 +32,7 @@ export async function fetchUserByEmailId(emailId: string) { if (status === 1) { logInfo('user account enabled. returning true') result.userExist = true + result.rootOrgId = contentObj.rootOrgId } else { logInfo('user account is diabled. throwing error') result.errMessage = 'Account Disabled. Please contact Admin.' @@ -69,7 +67,7 @@ export async function createUserWithMailId(emailId: string, firstNameStr: string Authorization: CONSTANTS.SB_API_KEY, }, method: 'POST', - url: API_END_POINTS.kongSignUpUser, + url: API_END_POINTS.cbExtSignUpUser, }) statusString = signUpResponse.data.params.status if (statusString.toUpperCase() !== 'SUCCESS') { @@ -78,67 +76,6 @@ export async function createUserWithMailId(emailId: string, firstNameStr: string } result.userCreated = true result.userId = signUpResponse.data.result.userId - const sbUserReadResponse = await axios({ - ...axiosRequestConfig, - headers: { - Authorization: CONSTANTS.SB_API_KEY, - }, - method: 'GET', - url: API_END_POINTS.kongUserRead(result.userId), - }) - statusString = sbUserReadResponse.data.params.status - if (statusString.toUpperCase() !== 'SUCCESS') { - result.errMessage = signUpErr + 'FAILED_TO_READ_CREATED_USER' - return Promise.resolve(result) - } - const sbUserOrgId = sbUserReadResponse.data.result.response.rootOrgId - const sbProfileUpdateReq = { - profileDetails: { - employmentDetails: { - departmentName: sbUserReadResponse.data.result.response.channel, - }, - personalDetails: { - firstname: firstNameStr, - primaryEmail: emailId, - surname: lastNameStr, - }, - }, - userId: result.userId, - } - const sbUserProfileUpdateResp = await axios({ - ...axiosRequestConfig, - data: { request: sbProfileUpdateReq }, - headers: { - Authorization: CONSTANTS.SB_API_KEY, - }, - method: 'PATCH', - url: API_END_POINTS.kongUpdateUser, - }) - statusString = sbUserProfileUpdateResp.data.params.status - if (statusString.toUpperCase() !== 'SUCCESS') { - result.errMessage = signUpErr + 'FAILED_TO_UPDATE_USER' - return Promise.resolve(result) - } - const sbAssignRoleResp = await axios({ - ...axiosRequestConfig, - data: { - request: { - organisationId: sbUserOrgId, - roles: ['PUBLIC'], - userId: result.userId, - }, - }, - headers: { - Authorization: CONSTANTS.SB_API_KEY, - }, - method: 'POST', - url: API_END_POINTS.kongAssignRoleUser, - }) - statusString = sbAssignRoleResp.data.params.status - if (statusString.toUpperCase() !== 'SUCCESS') { - result.errMessage = signUpErr + 'FAILED_TO_UPDATE_USER' - return Promise.resolve(result) - } return Promise.resolve(result) } diff --git a/src/server.ts b/src/server.ts index 76c634e5..a697f5b2 100644 --- a/src/server.ts +++ b/src/server.ts @@ -73,10 +73,13 @@ export class Server { } next() }) - this.app.use((_req: express.Request, res: express.Response, next: express.NextFunction) => { - res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate') - res.header('Expires', '-1') - res.header('Pragma', 'no-cache') + this.app.use((_req: express.Request, _res: express.Response, next: express.NextFunction) => { + // tslint:disable + // res.header('Cache-Control', 'private, no-cache, no-store, must-revalidate') + // res.header('Cache-Control', 'max-age=14400, must-revalidate') + // res.header('Expires', '-1') + // res.header('Pragma', 'no-cache') + // tslint:enable next() }) } diff --git a/src/utils/custom-keycloak.ts b/src/utils/custom-keycloak.ts index 146b1a33..2ad1d541 100644 --- a/src/utils/custom-keycloak.ts +++ b/src/utils/custom-keycloak.ts @@ -121,6 +121,19 @@ export class CustomKeycloak { Authorization: reqObj.session.parichayToken.access_token, }, url: CONSTANTS.PARICHAY_REVOKE_URL, + }, (err, res, body) => { + if (err) { + logError('Received error when calling Parichay logout... ') + logError(JSON.stringify(err)) + } + if (res) { + logInfo('Received response from Parichay logout... ') + logInfo(JSON.stringify(res.body)) + } + if (body) { + logInfo('Received body from Parichay logout...') + logInfo(JSON.stringify(body)) + } }) } catch (err) { // tslint:disable-next-line: no-console diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 01fba08b..1015b07b 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -21,6 +21,11 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { proxyReq.setHeader('Authorization', CONSTANTS.SB_API_KEY) proxyReq.setHeader('x-authenticated-user-token', extractUserToken(req)) proxyReq.setHeader('x-authenticated-userid', extractUserIdFromRequest(req)) + let rootOrgId = '' + if (req.session.hasOwnProperty('rootOrgId')) { + rootOrgId = req.session.rootOrgId + } + proxyReq.setHeader('x-authenticated-user-orgid', rootOrgId) // condition has been added to set the session in nodebb req header /* tslint:disable-next-line */ diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 3fe2c344..29621277 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -332,6 +332,34 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/forms/tagFormToCourse': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/forms/untagFormToCourse': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/forms/getAllApplications': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/forms/searchForms': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/upload/action/content/v3/upload/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1803,13 +1831,37 @@ export const API_LIST = { ROLE.PUBLIC, ], checksNeeded: [CHECK.ROLE], - }, + }, '/proxies/v8/ratings/v1/ratingLookUp': { - ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE_CHECK: [ + ROLE.PUBLIC, ], - checksNeeded: [CHECK.ROLE], - }, + checksNeeded: [CHECK.ROLE], + }, + '/proxies/v8/moderatoradmin/feedback/persist/text/moderated': { + ROLE_CHECK: [ + ROLE.SPV_ADMIN, + ], + checksNeeded: [CHECK.ROLE], + }, + '/proxies/v8/moderatoradmin/feedback/text/fetch': { + ROLE_CHECK: [ + ROLE.SPV_ADMIN, + ], + checksNeeded: [CHECK.ROLE], + }, + '/proxies/v8/moderatoradmin/profanity/type/text': { + ROLE_CHECK: [ + ROLE.SPV_ADMIN, + ], + checksNeeded: [CHECK.ROLE], + }, + '/proxies/v8/moderatoradmin/feedback/flag/values': { + ROLE_CHECK: [ + ROLE.SPV_ADMIN, + ], + checksNeeded: [CHECK.ROLE], + }, '/proxies/v8/user/v1/positions': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1839,6 +1891,20 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/user/v1/bulkupload': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ], + }, + '/proxies/v8/user/v1/bulkupload/:orgId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ], + }, }, URL_PATTERN: [ @@ -1930,6 +1996,10 @@ export const API_LIST = { '/proxies/v8/forms/getFormById', '/proxies/v8/forms/getAllForms', '/proxies/v8/forms/v1/saveFormSubmit', + '/proxies/v8/forms/getAllApplications', + '/proxies/v8/forms/tagFormToCourse', + '/proxies/v8/forms/untagFormToCourse', + '/proxies/v8/forms/searchForms', '/protected/v8/user/profileDetails/test', '/protected/v8/resource/', '/protected/v8/user/details', @@ -2079,9 +2149,15 @@ export const API_LIST = { '/proxies/v8/ratings/v1/upsert', '/proxies/v8/ratings/v1/summary/:activityId/:activityType', '/proxies/v8/ratings/v1/ratingLookUp', + '/proxies/v8/moderatoradmin/feedback/persist/text/moderated', + '/proxies/v8/moderatoradmin/feedback/text/fetch', + '/proxies/v8/moderatoradmin/profanity/type/text', + '/proxies/v8/moderatoradmin/feedback/flag/values', '/proxies/v8/user/v1/positions', '/proxies/v8/org/ext/v1/create', '/proxies/v8/user/basicInfo', '/proxies/v8/user/basicProfileUpdate', + '/proxies/v8/user/v1/bulkupload', + '/proxies/v8/user/v1/bulkupload/:orgId', ], } From 1a34e7343a2b3764a1a760feea21cf2236f282e2 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 6 Oct 2022 08:49:37 +0530 Subject: [PATCH 349/856] Added orgname header for bulk upload --- src/proxies_v8/proxies_v8.ts | 5 +++++ src/utils/proxyCreator.ts | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index d4cbfaac..c872f5fa 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -320,6 +320,10 @@ proxiesV8.post('/user/v1/bulkupload', (req, res) => { if (!rootOrgId) { rootOrgId = '' } + let channel = _.get(req, 'session.channel') + if (!channel) { + channel = '' + } formData.submit( { headers: { @@ -327,6 +331,7 @@ proxiesV8.post('/user/v1/bulkupload', (req, res) => { Authorization: CONSTANTS.SB_API_KEY, // tslint:disable-next-line: all 'x-authenticated-user-orgid': rootOrgId, + 'x-authenticated-user-orgname': channel, 'x-authenticated-user-token': extractUserToken(req), 'x-authenticated-userid': extractUserIdFromRequest(req), }, diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 1015b07b..08f10426 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -26,6 +26,11 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { rootOrgId = req.session.rootOrgId } proxyReq.setHeader('x-authenticated-user-orgid', rootOrgId) + let channel = '' + if (req.session.hasOwnProperty('channel')) { + channel = req.session.channel + } + proxyReq.setHeader('x-authenticated-user-orgname', channel) // condition has been added to set the session in nodebb req header /* tslint:disable-next-line */ From 8d1301094e67145226c90dd6c6b155053e5d6cdb Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Thu, 13 Oct 2022 18:05:21 +0530 Subject: [PATCH 350/856] 4.8.0 oct rel (#117) * 4.8.0 sep rel (#116) * Added MDO_ADMIN access for assessment create APIs * Removed - Bachelor of Science in Bible - as per NIC request * Added UID in header to backend service * updated issue cert api path * Task #0000: Added the new categories api to whitelist Co-authored-by: christopher.fernandes --- src/protectedApi_v8/cohorts.ts | 2 +- src/publicApi_v8/publicApiV8.ts | 2 +- src/static-data/degrees.json | 1 - src/utils/proxyCreator.ts | 1 + src/utils/whitelistApis.ts | 16 ++++++++++++++++ 5 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/protectedApi_v8/cohorts.ts b/src/protectedApi_v8/cohorts.ts index 0f7c51e3..12d424d0 100644 --- a/src/protectedApi_v8/cohorts.ts +++ b/src/protectedApi_v8/cohorts.ts @@ -16,7 +16,7 @@ const API_END_POINTS = { `${CONSTANTS.USER_PROFILE_API_BASE}/groups/${groupId}/users `, hierarchyApiEndPoint: (contentId: string) => `${CONSTANTS.KNOWLEDGE_MW_API_BASE}/action/content/v3/hierarchy/${contentId}?hierarchyType=detail`, - issueCert: '${CONSTANTS.HTTPS_HOST}/api/course/batch/cert/v1/issue?reIssue=true', + issueCert: `${CONSTANTS.KONG_API_BASE}/course/batch/cert/v1/issue?reIssue=true`, kongSearchUser: `${CONSTANTS.KONG_API_BASE}/user/v1/search`, searchUserRegistry: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/search/profile`, } diff --git a/src/publicApi_v8/publicApiV8.ts b/src/publicApi_v8/publicApiV8.ts index 2178d469..bb5844e3 100644 --- a/src/publicApi_v8/publicApiV8.ts +++ b/src/publicApi_v8/publicApiV8.ts @@ -19,4 +19,4 @@ publicApiV8.use('/workallocation', workallocationPublic) publicApiV8.use('/org/v1/list', proxyCreatorRoute(express.Router(), CONSTANTS.KONG_API_BASE + '/org/v1/list')) -publicApiV8.use('/parichay', parichayAuth) \ No newline at end of file +publicApiV8.use('/parichay', parichayAuth) diff --git a/src/static-data/degrees.json b/src/static-data/degrees.json index a2db558e..32ac3e3c 100644 --- a/src/static-data/degrees.json +++ b/src/static-data/degrees.json @@ -119,7 +119,6 @@ "Bachelor of Science in Athletic Training", "Bachelor of Science in Biology", "Bachelor of Science in Biomedical Engineering", - "Bachelor of Science in Bible", "Bachelor of Science in Business Administration", "Bachelor of Science in Business Administration - Computer Application", "Bachelor of Science in Business Administration - Economics", diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 08f10426..40b28820 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -31,6 +31,7 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { channel = req.session.channel } proxyReq.setHeader('x-authenticated-user-orgname', channel) + proxyReq.setHeader('x-authenticated-user-nodebb-uid', req.session.uid) // condition has been added to set the session in nodebb req header /* tslint:disable-next-line */ diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 29621277..70510c2d 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -612,6 +612,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/discussion/mainCategories': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/user/v1/autocomplete/:key': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1749,11 +1756,13 @@ export const API_LIST = { ROLE.CONTENT_CREATOR, ROLE.CONTENT_PUBLISHER, ROLE.CONTENT_REVIEWER, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/questionset/v1/create': { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, + ROLE.MDO_ADMIN, ], checksNeeded: [CHECK.ROLE], }, @@ -1762,6 +1771,7 @@ export const API_LIST = { ROLE.CONTENT_CREATOR, ROLE.CONTENT_PUBLISHER, ROLE.CONTENT_REVIEWER, + ROLE.MDO_ADMIN, ], checksNeeded: [CHECK.ROLE], }, @@ -1770,24 +1780,28 @@ export const API_LIST = { ROLE.CONTENT_CREATOR, ROLE.CONTENT_PUBLISHER, ROLE.CONTENT_REVIEWER, + ROLE.MDO_ADMIN, ], checksNeeded: [CHECK.ROLE], }, '/proxies/v8/questionset/v1/review/:id': { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, + ROLE.MDO_ADMIN, ], checksNeeded: [CHECK.ROLE], }, '/proxies/v8/questionset/v1/publish/:id': { ROLE_CHECK: [ ROLE.CONTENT_PUBLISHER, + ROLE.MDO_ADMIN, ], checksNeeded: [CHECK.ROLE], }, '/proxies/v8/questionset/v1/hierarchy/update': { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, + ROLE.MDO_ADMIN, ], checksNeeded: [CHECK.ROLE], }, @@ -1811,6 +1825,7 @@ export const API_LIST = { ROLE.CONTENT_CREATOR, ROLE.CONTENT_PUBLISHER, ROLE.CONTENT_REVIEWER, + ROLE.MDO_ADMIN, ], checksNeeded: [CHECK.ROLE], }, @@ -1982,6 +1997,7 @@ export const API_LIST = { '/proxies/v8/discussion/user/:userKey/upvoted', '/proxies/v8/discussion/user/:userKey/downvoted', '/proxies/v8/discussion/categories', + '/proxies/v8/discussion/mainCategories', '/proxies/v8/user/v1/autocomplete/:key', '/proxies/v8/user/v1/migrate', '/proxies/v8/user/private/v1/migrate', From 7351f49bcdfa92547612bdd642b3911cfb1d73f6 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Mon, 31 Oct 2022 15:17:57 +0530 Subject: [PATCH 351/856] 4.8.0 oct rel (#120) * 4.8.0 sep rel (#116) * Added MDO_ADMIN access for assessment create APIs * Removed - Bachelor of Science in Bible - as per NIC request * Added UID in header to backend service * updated issue cert api path * Task #0000: Added the new categories api to whitelist * List of new Graduations (#118) Co-authored-by: christopher.fernandes Co-authored-by: Sreerag K S <58926794+sreeragksgh@users.noreply.github.com> --- src/static-data/degrees.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/static-data/degrees.json b/src/static-data/degrees.json index 32ac3e3c..1e7352ee 100644 --- a/src/static-data/degrees.json +++ b/src/static-data/degrees.json @@ -199,7 +199,15 @@ "Bachelor of Music in Theory", "Bachelor of Music in Music Education", "Bachelor of Science in Veterinary Technology", - "Bachelor of Science in military and strategic studies" + "Bachelor of Science in military and strategic studies", + "Bachelor of Education - Special Education", + "Bachelor in Prosthetics & Orthotics", + "Bachelor in Audiology and Speech Language Pathology", + "Bachelor in Rehabilitation Science", + "Shastri/Shastri(Hons.)", + "Shiksha Shastri", + "Fazil", + "Afzal-Ul-Ulma" ], "postGraduations": [ "Master of Accountancy", From c867020876fb88b572170c8a8c5788c469654722 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Mon, 14 Nov 2022 09:31:06 +0530 Subject: [PATCH 352/856] added APIs for OTP generate and verify (#121) --- src/proxies_v8/proxies_v8.ts | 4 ++++ src/utils/whitelistApis.ts | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index c872f5fa..1f27e60d 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -363,6 +363,10 @@ proxiesV8.use('/user/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) +proxiesV8.use('/otp/*', + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + proxiesV8.use('/event/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 70510c2d..477732c5 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1920,6 +1920,20 @@ export const API_LIST = { ROLE.MDO_ADMIN, ], }, + '/proxies/v8/otp/v1/generate': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/otp/v1/verify': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -2175,5 +2189,7 @@ export const API_LIST = { '/proxies/v8/user/basicProfileUpdate', '/proxies/v8/user/v1/bulkupload', '/proxies/v8/user/v1/bulkupload/:orgId', + '/proxies/v8/otp/v1/generate', + '/proxies/v8/otp/v1/otp', ], } From 4ab182a914d8313ca805538929ceb67d677cce15 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Sat, 7 Jan 2023 22:37:12 +0530 Subject: [PATCH 353/856] 4.8.0 user read fix (#128) * Fix for user Read API --- src/proxies_v8/proxies_v8.ts | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 1f27e60d..91894167 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -209,9 +209,12 @@ proxiesV8.get(['/api/user/v2/read', '/api/user/v2/read/:id'], async (req, res) = const originalUrl = req.originalUrl const lastIndex = originalUrl.lastIndexOf('/') const subStr = originalUrl.substr(lastIndex).substr(1).split('-').length - let userId = extractUserIdFromRequest(req).split(':')[2] + const loggedInUserId = extractUserIdFromRequest(req).split(':')[2] + let urlUserId = '' + let userId = loggedInUserId if (subStr === 5 && (originalUrl.substr(lastIndex).substr(1))) { - userId = originalUrl.substr(lastIndex).substr(1) + urlUserId = originalUrl.substr(lastIndex).substr(1) + userId = urlUserId } await axios({ @@ -228,21 +231,30 @@ proxiesV8.get(['/api/user/v2/read', '/api/user/v2/read/:id'], async (req, res) = res.status(200).send(response.data) } else { logError('User Read API.. Received non OK response.' + JSON.stringify(response.data)) - res.redirect(`https://${host}/public/logout?error=` + encodeURIComponent(JSON.stringify(response.data.params.errmsg))) + if (urlUserId.length > 1 && urlUserId !== loggedInUserId) { + res.status(400).send(response.data) + } else { + res.redirect(`https://${host}/public/logout?error=` + encodeURIComponent(JSON.stringify(response.data.params.errmsg))) + } } }).catch((err) => { + logError('Failed to do user read API. Received Exception: loggedInUserId : ' + loggedInUserId + ', urlUserId: ' + urlUserId) let errMsg = 'Internal Server Error' if (err.response && err.response.data) { logError('Received error for user read API. Error: ' + JSON.stringify(err.response.data)) errMsg = err.response.data.params.errmsg } - if (req.session) { - req.session.destroy((dErr) => { - logError('Failed to clear the session. ERROR: ' + JSON.stringify(dErr)) - }) + if (urlUserId.length > 1 && urlUserId !== loggedInUserId) { + res.status(400).send(err.response.data) + } else { + if (req.session) { + req.session.destroy((dErr) => { + logError('Failed to clear the session. ERROR: ' + JSON.stringify(dErr)) + }) + } + res.clearCookie('connect.sid', { path: '/' }) + res.redirect(`https://${host}/public/logout?error=` + encodeURIComponent(errMsg)) } - res.clearCookie('connect.sid', { path: '/' }) - res.redirect(`https://${host}/public/logout?error=` + encodeURIComponent(errMsg)) }) }) From a1ebf058b206bfd970a8d50a10cd9df091759e19 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Mon, 13 Feb 2023 08:05:21 +0530 Subject: [PATCH 354/856] 4.8.0 jan23 rel (#130) * 4.8.0 oct rel v3 (#129) Merging changes done in 4.8.0-octRelV3 * Added route to mdo content APIs * mdo api whitelist * Added debug logs * Added APIs for mdo content management --------- Co-authored-by: santhosh-tarento --- src/protectedApi_v8/user/profile-details.ts | 4 +- src/proxies_v8/proxies_v8.ts | 4 + src/utils/proxyCreator.ts | 7 +- src/utils/whitelistApis.ts | 82 +++++++++++++++++++++ 4 files changed, 92 insertions(+), 5 deletions(-) diff --git a/src/protectedApi_v8/user/profile-details.ts b/src/protectedApi_v8/user/profile-details.ts index 59a1fad3..bcaf26dd 100644 --- a/src/protectedApi_v8/user/profile-details.ts +++ b/src/protectedApi_v8/user/profile-details.ts @@ -375,14 +375,14 @@ profileDeatailsApi.post('/createUser', async (req, res) => { const welcomeMailRequest = { allowedLoging: 'You can use your email to Login', body: 'Hello', - emailTemplateType: 'iGotWelcome', + emailTemplateType: 'iGotWelcome_v3', firstName: sbUserProfile.firstName, link: passwordResetResponse.data.result.link, mode: 'email', orgName: sbChannel, recipientEmails: [ sbemail_ ], setPasswordLink: true, - subject: 'Welcome Email', + subject: 'Welcome to iGOT Karmayogi... Activate your account now!', welcomeMessage: 'Hello', } diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 91894167..830b5600 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -279,6 +279,10 @@ proxiesV8.use('/action/*', proxyCreatorKnowledge(express.Router(), `${CONSTANTS.KNOWLEDGE_MW_API_BASE}`) ) +proxiesV8.use('/mdo/content/*', + proxyCreatorKnowledge(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + proxiesV8.use('/learner/*', // tslint:disable-next-line: max-line-length proxyCreatorLearner(express.Router(), `${CONSTANTS.KONG_API_BASE}`) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 40b28820..9896f887 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -1,6 +1,6 @@ import { Router } from 'express' import { createProxyServer } from 'http-proxy' -import { extractUserEmailFromRequest, extractUserIdFromRequest, extractUserToken } from '../utils/requestExtract' +import { extractUserEmailFromRequest, extractUserId, extractUserToken } from '../utils/requestExtract' import { CONSTANTS } from './env' import { logInfo } from './logger' @@ -15,12 +15,13 @@ const PROXY_SLUG_WAT = '/proxies/v8/wat' // tslint:disable-next-line: no-any proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { + logInfo('proxyReqOn method. Adding more headers in request...') // tslint:disable-next-line: no-duplicate-string proxyReq.setHeader('X-Channel-Id', (_.get(req, 'session.rootOrgId')) ? _.get(req, 'session.rootOrgId') : CONSTANTS.X_Channel_Id) // tslint:disable-next-line: max-line-length proxyReq.setHeader('Authorization', CONSTANTS.SB_API_KEY) proxyReq.setHeader('x-authenticated-user-token', extractUserToken(req)) - proxyReq.setHeader('x-authenticated-userid', extractUserIdFromRequest(req)) + proxyReq.setHeader('x-authenticated-userid', extractUserId(req)) let rootOrgId = '' if (req.session.hasOwnProperty('rootOrgId')) { rootOrgId = req.session.rootOrgId @@ -243,7 +244,7 @@ export function proxyCreatorToAppentUserId(route: Router, targetUrl: string, _ti const originalUrl = req.originalUrl const lastIndex = originalUrl.lastIndexOf('/') const subStr = originalUrl.substr(lastIndex).substr(1).split('-').length - let userId = extractUserIdFromRequest(req).split(':')[2] + let userId = extractUserId(req) if (subStr === 5 && (originalUrl.substr(lastIndex).substr(1))) { userId = originalUrl.substr(lastIndex).substr(1) } diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 477732c5..dfe39b40 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -360,6 +360,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/forms/getCourseListForSurveys': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/upload/action/content/v3/upload/:do_id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -406,6 +413,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.CONTENT_PUBLISHER, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/action/content/v3/update/:do_id': { @@ -416,6 +424,7 @@ export const API_LIST = { ROLE.EDITOR, ROLE.CONTENT_REVIEWER, ROLE.CONTENT_PUBLISHER, + ROLE.MDO_ADMIN, ], }, '/proxies/v8/data/v1/system/settings/get/orgTypeList': { @@ -428,6 +437,13 @@ export const API_LIST = { ROLE.STATE_ADMIN, ], }, + '/proxies/v8/data/v1/system/settings/get/notificationPreference': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/org/v1/search': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -1934,6 +1950,62 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/user/v1/notificationPreference': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/user/assessment/retake/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/mdo/content/v3/create': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ], + }, + '/proxies/v8/mdo/content/v3/hierarchy/update': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ], + }, + '/proxies/v8/mdo/content/v3/update/:do_id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ], + }, + '/proxies/v8/mdo/content/v3/publish/:do_id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ], + }, + '/proxies/v8/mdo/content/v3/batch/addUser': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ], + }, + '/proxies/v8/mdo/content/v3/batch/removeUser': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ], + }, }, URL_PATTERN: [ @@ -1957,6 +2029,7 @@ export const API_LIST = { '/proxies/v8/sunbirdigot/search', '/proxies/v8/discussion/user/v1/create', '/proxies/v8/data/v1/system/settings/get/orgTypeList', + '/proxies/v8/data/v1/system/settings/get/notificationPreference', '/proxies/v8/org/v1/search', '/proxies/v8/org/v1/update', '/proxies/v8/notifyContentState', @@ -2030,6 +2103,7 @@ export const API_LIST = { '/proxies/v8/forms/tagFormToCourse', '/proxies/v8/forms/untagFormToCourse', '/proxies/v8/forms/searchForms', + '/proxies/v8/forms/getCourseListForSurveys', '/protected/v8/user/profileDetails/test', '/protected/v8/resource/', '/protected/v8/user/details', @@ -2191,5 +2265,13 @@ export const API_LIST = { '/proxies/v8/user/v1/bulkupload/:orgId', '/proxies/v8/otp/v1/generate', '/proxies/v8/otp/v1/otp', + '/proxies/v8/user/v1/notificationPreference', + '/proxies/v8/user/assessment/retake/:id', + '/proxies/v8/mdo/content/v3/create', + '/proxies/v8/mdo/content/v3/hierarchy/update', + '/proxies/v8/mdo/content/v3/update/:do_id', + '/proxies/v8/mdo/content/v3/publish/:do_id', + '/proxies/v8/mdo/content/v3/batch/addUser', + '/proxies/v8/mdo/content/v3/batch/removeUser', ], } From 4c565b4ea763c57f74f6142b404d39298ff1830e Mon Sep 17 00:00:00 2001 From: juhi agarwal Date: Tue, 21 Feb 2023 18:25:05 +0530 Subject: [PATCH 355/856] Changes for the Content Flagging APIs (#134) * Update whitelistApis.ts (#132) --- src/utils/whitelistApis.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index dfe39b40..9c572c14 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2006,6 +2006,20 @@ export const API_LIST = { ROLE.MDO_ADMIN, ], }, + '/proxies/v8/user/offensive/data/flag': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/user/offensive/data/flag/getFlaggedData': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -2273,5 +2287,7 @@ export const API_LIST = { '/proxies/v8/mdo/content/v3/publish/:do_id', '/proxies/v8/mdo/content/v3/batch/addUser', '/proxies/v8/mdo/content/v3/batch/removeUser', + '/proxies/v8/user/offensive/data/flag', + '/proxies/v8/user/offensive/data/flag/getFlaggedData', ], } From 9ea69993aad147d8a69a4ce7165c9bc3460f258b Mon Sep 17 00:00:00 2001 From: vikrantbipin <118338417+vikrantbipin@users.noreply.github.com> Date: Fri, 10 Mar 2023 12:53:50 +0530 Subject: [PATCH 356/856] Update degrees.json (#135) Added 1 degree in graduation. --- src/static-data/degrees.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/static-data/degrees.json b/src/static-data/degrees.json index 1e7352ee..f87e2782 100644 --- a/src/static-data/degrees.json +++ b/src/static-data/degrees.json @@ -207,7 +207,8 @@ "Shastri/Shastri(Hons.)", "Shiksha Shastri", "Fazil", - "Afzal-Ul-Ulma" + "Afzal-Ul-Ulma", + "Bachelor of Laws" ], "postGraduations": [ "Master of Accountancy", @@ -390,4 +391,4 @@ "Doctor of Veterinary Medicine", "Doctor of Radio oncology" ] -} \ No newline at end of file +} From d843819435cc8a348230b60c94d1311641c2b0f3 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Mon, 13 Mar 2023 15:35:58 +0530 Subject: [PATCH 357/856] 4.8.0 mar 23 rel (#137) * Using secure and sameSite attributes in Cookie --- src/server.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/server.ts b/src/server.ts index a697f5b2..ce528507 100644 --- a/src/server.ts +++ b/src/server.ts @@ -70,6 +70,8 @@ export class Server { const rootOrg = req.headers ? req.headers.rootOrg || req.headers.rootorg : '' if (rootOrg && req.hostname.toLowerCase().includes('localhost')) { res.cookie('rootorg', rootOrg) + res.cookie('secure', true) + res.cookie('sameSite', 'Lax') } next() }) From 586f3dd10d5e4ca5dc3742bf46522892f2e9539e Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Thu, 23 Mar 2023 17:21:56 +0530 Subject: [PATCH 358/856] Updated log statement for Https redirection --- src/server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server.ts b/src/server.ts index ce528507..02fcc25d 100644 --- a/src/server.ts +++ b/src/server.ts @@ -113,7 +113,7 @@ export class Server { ) // TODO: See what needs to be logged this.app.use((req, _, next) => { - logInfo(`Server:ConfigureMiddleWare:: Worker ${process.pid} : ${req.url}`) + logInfo(`Server:ConfigureMiddleWare:: Worker ${process.pid} : ${req.protocol}://${req.hostname}/${req.url}`) next() }) this.app.use(morgan('dev')) From 96ba25ff76677cdb1b8d3cced348a667ab532463 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Mon, 3 Apr 2023 15:46:20 +0530 Subject: [PATCH 359/856] Added secure and sameSite flags in cookie --- src/protectedApi_v8/resource.ts | 12 +++++++++++- src/server.ts | 4 ++-- src/utils/custom-keycloak.ts | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/protectedApi_v8/resource.ts b/src/protectedApi_v8/resource.ts index 2c7cbd92..b4ecd513 100644 --- a/src/protectedApi_v8/resource.ts +++ b/src/protectedApi_v8/resource.ts @@ -1,4 +1,5 @@ import { Router } from 'express' +import { CONSTANTS } from '../utils/env' import { logInfo } from '../utils/logger' const _ = require('lodash') export const userAuthKeyCloakApi = Router() @@ -6,7 +7,16 @@ userAuthKeyCloakApi.get('/', (req, res) => { const host = req.get('host') let queryParam = '' let isLocal = 0 - logInfo('Received query param: ' + req.query) + logInfo('Received query param: ' + JSON.stringify(req.query)) + if (req.session && req.session.authenticated ) { + logInfo('User is authenticated.. Updating Cookie with Secure and SameSite flags') + res.cookie('connect.sid', req.cookies['connect.sid'], { + httpOnly: true, + maxAge: CONSTANTS.KEYCLOAK_SESSION_TTL, + sameSite: 'None', + secure: true, + }) + } if (!_.isEmpty(req.query)) { queryParam = req.query.q if (queryParam && queryParam.includes('localhost')) { diff --git a/src/server.ts b/src/server.ts index 02fcc25d..3132c7ac 100644 --- a/src/server.ts +++ b/src/server.ts @@ -70,8 +70,6 @@ export class Server { const rootOrg = req.headers ? req.headers.rootOrg || req.headers.rootorg : '' if (rootOrg && req.hostname.toLowerCase().includes('localhost')) { res.cookie('rootorg', rootOrg) - res.cookie('secure', true) - res.cookie('sameSite', 'Lax') } next() }) @@ -113,6 +111,8 @@ export class Server { ) // TODO: See what needs to be logged this.app.use((req, _, next) => { + logInfo('adding x-forward-proto header with https to request...') + req.headers['x-forwarded-proto'] = 'https' logInfo(`Server:ConfigureMiddleWare:: Worker ${process.pid} : ${req.protocol}://${req.hostname}/${req.url}`) next() }) diff --git a/src/utils/custom-keycloak.ts b/src/utils/custom-keycloak.ts index 2ad1d541..25c918e5 100644 --- a/src/utils/custom-keycloak.ts +++ b/src/utils/custom-keycloak.ts @@ -57,6 +57,7 @@ export class CustomKeycloak { // tslint:disable-next-line: no-any authenticated = (reqObj: any, next: any) => { logInfo('Step 3: authenticated function', '------', new Date().toString()) + reqObj.session.authenticated = true try { const userId = reqObj.kauth.grant.access_token.content.sub.split(':') reqObj.session.userId = userId[userId.length - 1] From d20e6b96978b5e1e13963c2fd11f978bcf43b64a Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Wed, 5 Apr 2023 18:15:00 +0530 Subject: [PATCH 360/856] 4.8.0 mar23 rel v2 (#147) (#148) Merged code from 4.8.0-mar23Rel-v2 branch --- src/protectedApi_v8/user/profile-registry.ts | 193 ++++--------------- src/proxies_v8/proxies_v8.ts | 4 + src/utils/whitelistApis.ts | 20 +- 3 files changed, 63 insertions(+), 154 deletions(-) diff --git a/src/protectedApi_v8/user/profile-registry.ts b/src/protectedApi_v8/user/profile-registry.ts index 179e4c1a..a7842c08 100644 --- a/src/protectedApi_v8/user/profile-registry.ts +++ b/src/protectedApi_v8/user/profile-registry.ts @@ -6,13 +6,15 @@ import { logError, logInfo } from '../../utils/logger' import { extractAuthorizationFromRequest, extractUserIdFromRequest, IAuthorizedRequest } from '../../utils/requestExtract' -const fs = require('fs') const API_END_POINTS = { createUserRegistry: (userId: string) => `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/create/profile?userId=${userId}`, getAllPosition: `${CONSTANTS.FRAC_API_BASE}/frac/getAllNodes?type=POSITION&status=VERIFIED`, getUserRegistry: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/get/profile`, getUserRegistryById: (userId: string) => `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/search/profile?userId=${userId}`, + masterLanguages: `${CONSTANTS.KONG_API_BASE}/masterData/v1/languages`, + masterNationalities: `${CONSTANTS.KONG_API_BASE}/masterData/v1/nationalities`, + profilePageMetaData: `${CONSTANTS.KONG_API_BASE}/masterData/v1/profilePageMetaData`, searchUserRegistry: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/search/profile`, updateUserRegistry: (userId: string) => `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/update/profile?userId=${userId}`, updateUserWorkflowRegistry: (userId: string) => @@ -25,6 +27,8 @@ const profileStatusCheckConfig = { } const ERROR_MESSAGE_CREATE_REGISTRY = 'ERROR CREATING USER REGISTRY >' +const CONNECTION_ERROR = 'CONNECTIONS REQUESTS ERROR> ' +const unknown = 'Connections Apis:- Failed due to unknown reason' export const profileRegistryApi = Router() @@ -152,176 +156,61 @@ profileRegistryApi.get('/getUserRegistryByUser/:id', async (req, res) => { profileRegistryApi.get('/getMasterNationalities', async (_req, res) => { try { - // tslint:disable-next-line: no-identical-functions - fs.readFile(__dirname + '/../../static-data/nationality.json', (err: Error, json: string) => { - if (!err) { - const obj = JSON.parse(json) - res.json(obj) - } + const response = await axios.get(API_END_POINTS.masterNationalities, { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + }, }) + res.send((response.data)) } catch (err) { - res.status((err && err.response && err.response.status) || 500).send(err) + logError(CONNECTION_ERROR, err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: unknown, + } + ) } }) profileRegistryApi.get('/getMasterLanguages', async (_req, res) => { try { - fs.readFile(__dirname + '/../../static-data/languages.json', (err: Error, json: string) => { - if (!err) { - const obj = JSON.parse(json) - res.json({ - languages: obj.languages.map((item: string) => { - return { name: item } - }), - }) - } + const response = await axios.get(API_END_POINTS.masterLanguages, { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + }, }) + res.send((response.data)) } catch (err) { - res.status((err && err.response && err.response.status) || 500).send(err) + logError(CONNECTION_ERROR, err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: unknown, + } + ) } }) profileRegistryApi.get('/getProfilePageMeta', async (_req, res) => { try { - const govtOrg = await govtOrgMeta() - .catch((err) => { - logError(`error fetching govtOrgMeta`, err) - }) - const industries = await industreisMeta() - .catch((err) => { - logError(`error fetching industreisMeta`, err) - }) - const degrees = await degreesMeta() - .catch((err) => { - logError(`error fetching degreesMeta`, err) - }) - let designations = Object.create({}) - designations = await designationMeta() - .catch((err) => { - logError(`error fetching designationMeta`, err) - }) - designations.designations = await designationMetaFrac(_req) - .catch((err) => { - logError('error fetching desingations from FRAC', err) - }) - res.json({ - degrees, - designations, - govtOrg, - industries, + const response = await axios.get(API_END_POINTS.profilePageMetaData, { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + }, }) + res.send((response.data)) } catch (err) { - res.status((err && err.response && err.response.status) || 500).send(err) + logError(CONNECTION_ERROR, err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: unknown, + } + ) } }) -export async function govtOrgMeta() { - return new Promise(async (resolve, reject) => { - try { - await fs.readFile(__dirname + '/../../static-data/govtOrg.json', (err: Error, json: string) => { - if (!err) { - const obj = JSON.parse(json) - const result = { - cadre: obj.cadre.map((item: string) => { - return { name: item } - }), - ministries: obj.ministries.map((item: string) => { - return { name: item } - }), - service: obj.services.map((item: string) => { - return { name: item } - }), - } - resolve(result) - } else { - reject(err) - - } - }) - } catch (err) { - logError('ERROR on govtOrgMeta') - throw err - } - }) -} - -export async function industreisMeta() { - return new Promise(async (resolve, reject) => { - try { - await fs.readFile(__dirname + '/../../static-data/industries.json', (err: Error, json: string) => { - if (!err) { - const obj = JSON.parse(json) - resolve( - obj.industries.map((item: string) => { - return { name: item } - }) - ) - } else { - reject(err) - - } - }) - } catch (err) { - logError('ERROR on industreisMeta') - throw err - } - }) -} - -export async function degreesMeta() { - return new Promise(async (resolve, reject) => { - try { - await fs.readFile(__dirname + '/../../static-data/degrees.json', (err: Error, json: string) => { - if (!err) { - const obj = JSON.parse(json) - const result = { - graduations: obj.graduations.map((item: string) => { - return { name: item } - }), - postGraduations: obj.postGraduations.map((item: string) => { - return { name: item } - }), - } - resolve(result) - } else { - reject(err) - - } - }) - } catch (err) { - logError('ERROR on degreesMeta') - throw err - } - }) -} - -export async function designationMeta() { - return new Promise(async (resolve, reject) => { - try { - await fs.readFile(__dirname + '/../../static-data/designation.json', (err: Error, json: string) => { - if (!err) { - const obj = JSON.parse(json) - const result = { - designations: obj.designations.map((item: string) => { - return { name: item } - }), - gradePay: obj.gradePay.map((item: string) => { - return { name: item } - }), - } - resolve(result) - } else { - reject(err) - - } - }) - } catch (err) { - logError('ERROR on designationMeta') - throw err - } - }) -} - profileRegistryApi.post('/createUserRegistryV2/:userId', async (req, res) => { try { const userId = req.params.userId diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 830b5600..150f78d7 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -412,6 +412,10 @@ proxiesV8.use('/forms/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) +proxiesV8.use('/masterData/*', + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + // proxiesV8.use('/api/framework/*', // // tslint:disable-next-line: max-line-length // proxyCreatorQML(express.Router(), `${CONSTANTS.KONG_API_BASE}`, '/api/') diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 9c572c14..0a320423 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -318,6 +318,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/forms/getCollectiveAggregation': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/proxies/v8/forms/getAllForms': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -2006,14 +2013,21 @@ export const API_LIST = { ROLE.MDO_ADMIN, ], }, - '/proxies/v8/user/offensive/data/flag': { + '/proxies/v8/user/offensive/data/flag': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/user/offensive/data/flag/getFlaggedData': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.PUBLIC, ], }, - '/proxies/v8/user/offensive/data/flag/getFlaggedData': { + '/proxies/v8/masterData/v1/upsert': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -2111,6 +2125,7 @@ export const API_LIST = { '/proxies/v8/wat/dashboard/getDashboardConfig/Karmayogi/overview', '/proxies/v8/forms/createForm', '/proxies/v8/forms/getFormById', + '/proxies/v8/forms/getCollectiveAggregation', '/proxies/v8/forms/getAllForms', '/proxies/v8/forms/v1/saveFormSubmit', '/proxies/v8/forms/getAllApplications', @@ -2289,5 +2304,6 @@ export const API_LIST = { '/proxies/v8/mdo/content/v3/batch/removeUser', '/proxies/v8/user/offensive/data/flag', '/proxies/v8/user/offensive/data/flag/getFlaggedData', + '/proxies/v8/masterData/v1/upsert', ], } From 0957d4a61f22f28f70dfe69a474e4ca005cfda41 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Fri, 21 Apr 2023 20:31:07 +0530 Subject: [PATCH 361/856] 4.8.0 bulk upload fix (#149) * Added APIs for bulkupload download API * Updated bulk user download API path to avoid confusion --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 0a320423..5a445d69 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2034,6 +2034,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/user/v1/bulkuser/download/:id' : { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ], + }, }, URL_PATTERN: [ @@ -2305,5 +2312,6 @@ export const API_LIST = { '/proxies/v8/user/offensive/data/flag', '/proxies/v8/user/offensive/data/flag/getFlaggedData', '/proxies/v8/masterData/v1/upsert', + '/proxies/v8/user/v1/bulkuser/download/:id', ], } From 6967cfca579db0b10af265341b821627ea2a4d45 Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Sun, 23 Apr 2023 18:37:55 +0530 Subject: [PATCH 362/856] Added check to avoid null (#150) --- src/protectedApi_v8/cohorts.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/protectedApi_v8/cohorts.ts b/src/protectedApi_v8/cohorts.ts index 12d424d0..bdec43ce 100644 --- a/src/protectedApi_v8/cohorts.ts +++ b/src/protectedApi_v8/cohorts.ts @@ -292,12 +292,15 @@ cohortsApi.get('/course/getUsersForBatch/:batchId', async (req, res) => { } }) +// tslint:disable-next-line: all function getUsers(userprofile: IUserProfile): ICohortsUser { let designationValue = '' let primaryEmail = '' let mobileNumber = 0 - if (userprofile.profileDetails !== undefined) { - if (userprofile.profileDetails.professionalDetails !== undefined && userprofile.profileDetails.professionalDetails.length > 0) { + const profileDetails = userprofile.hasOwnProperty('profileDetails') ? userprofile.profileDetails : null + if (profileDetails != null) { + const professionalDetails = profileDetails.hasOwnProperty('professionalDetails') ? profileDetails.professionalDetails : null + if (professionalDetails != null) { if (userprofile.profileDetails.professionalDetails[0].designation !== undefined) { designationValue = userprofile.profileDetails.professionalDetails[0].designation } else { @@ -310,6 +313,7 @@ function getUsers(userprofile: IUserProfile): ICohortsUser { mobileNumber = userprofile.profileDetails.personalDetails.mobile } } + return { city: '', department: userprofile.channel === undefined ? '' : userprofile.channel, From d6e0b1eca902e8383693a802a053a931f1dee168 Mon Sep 17 00:00:00 2001 From: Haritest Date: Sun, 23 Apr 2023 21:09:33 +0530 Subject: [PATCH 363/856] apt update fix --- Dockerfile.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.build b/Dockerfile.build index 69ec82d7..96c4f8b9 100644 --- a/Dockerfile.build +++ b/Dockerfile.build @@ -3,7 +3,7 @@ FROM node:12 WORKDIR /usr/src/app COPY . . -RUN apt update && apt install -y zip +RUN sed -i 's/stretch/buster/' /etc/apt/sources.list && apt update && apt install -y zip RUN npm install && npm run build RUN zip -r dist.zip dist From be430ff9515b3c0ea1921b51a96159f45a2a7b7c Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Fri, 28 Apr 2023 17:34:34 +0530 Subject: [PATCH 364/856] Added channel header for bulk upload (#151) --- src/proxies_v8/proxies_v8.ts | 1 + src/utils/proxyCreator.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 150f78d7..8b0e5066 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -346,6 +346,7 @@ proxiesV8.post('/user/v1/bulkupload', (req, res) => { // tslint:disable-next-line:max-line-length Authorization: CONSTANTS.SB_API_KEY, // tslint:disable-next-line: all + 'x-authenticated-user-channel': channel, 'x-authenticated-user-orgid': rootOrgId, 'x-authenticated-user-orgname': channel, 'x-authenticated-user-token': extractUserToken(req), diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 9896f887..5aba37ae 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -32,6 +32,7 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { channel = req.session.channel } proxyReq.setHeader('x-authenticated-user-orgname', channel) + proxyReq.setHeader('x-authenticated-user-channel', channel) proxyReq.setHeader('x-authenticated-user-nodebb-uid', req.session.uid) // condition has been added to set the session in nodebb req header From 2c6a2d7b7bd84865b1cc036ccb3f8137e681ce90 Mon Sep 17 00:00:00 2001 From: Manas-tarento Date: Fri, 5 May 2023 16:19:47 +0530 Subject: [PATCH 365/856] wf search and update api --- src/proxies_v8/proxies_v8.ts | 4 +++ src/utils/whitelistApis.ts | 54 ++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 150f78d7..e951a3e6 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -514,6 +514,10 @@ proxiesV8.use('/moderatoradmin/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) +proxiesV8.use('/workflow/*', + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + function removePrefix(prefix: string, s: string) { return s.substr(prefix.length) } diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 5a445d69..24927916 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2041,6 +2041,54 @@ export const API_LIST = { ROLE.MDO_ADMIN, ], }, + '/proxies/v8/workflow/position/update': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.SPV_ADMIN, + ROLE.MDO_ADMIN, + ], + }, + '/proxies/v8/workflow/org/update': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.SPV_ADMIN, + ROLE.MDO_ADMIN, + ], + }, + '/proxies/v8/workflow/domain/update': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.SPV_ADMIN, + ROLE.MDO_ADMIN, + ], + }, + '/proxies/v8/workflow/position/search': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.SPV_ADMIN, + ROLE.MDO_ADMIN, + ], + }, + '/proxies/v8/workflow/org/search': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.SPV_ADMIN, + ROLE.MDO_ADMIN, + ], + }, + '/proxies/v8/workflow/domain/search': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.SPV_ADMIN, + ROLE.MDO_ADMIN, + ], + }, }, URL_PATTERN: [ @@ -2313,5 +2361,11 @@ export const API_LIST = { '/proxies/v8/user/offensive/data/flag/getFlaggedData', '/proxies/v8/masterData/v1/upsert', '/proxies/v8/user/v1/bulkuser/download/:id', + '/proxies/v8/workflow/position/update', + '/proxies/v8/workflow/org/update', + '/proxies/v8/workflow/domain/update', + '/proxies/v8/workflow/position/search', + '/proxies/v8/workflow/org/search', + '/proxies/v8/workflow/domain/search' ], } From 0e801dedeef7b2d932d1bbe72606a4b9ad2e821c Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Mon, 15 May 2023 17:25:26 +0530 Subject: [PATCH 366/856] SameSite value set to Lax (#155) --- src/protectedApi_v8/resource.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protectedApi_v8/resource.ts b/src/protectedApi_v8/resource.ts index b4ecd513..0f3923a3 100644 --- a/src/protectedApi_v8/resource.ts +++ b/src/protectedApi_v8/resource.ts @@ -13,7 +13,7 @@ userAuthKeyCloakApi.get('/', (req, res) => { res.cookie('connect.sid', req.cookies['connect.sid'], { httpOnly: true, maxAge: CONSTANTS.KEYCLOAK_SESSION_TTL, - sameSite: 'None', + sameSite: 'Lax', secure: true, }) } From c9b1cb2105f6562860ce719bf5e14c2283a6e24a Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Tue, 6 Jun 2023 17:43:23 +0530 Subject: [PATCH 367/856] Added user orgid header for autoenrolment --- src/protectedApi_v8/cohorts.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/protectedApi_v8/cohorts.ts b/src/protectedApi_v8/cohorts.ts index bdec43ce..2f3ef268 100644 --- a/src/protectedApi_v8/cohorts.ts +++ b/src/protectedApi_v8/cohorts.ts @@ -146,6 +146,12 @@ cohortsApi.get('/user/autoenrollment/:courseId', async (req, res) => { const courseId = req.params.courseId const wid = req.headers.wid as string const rootOrgValue = req.headers.rootorg + let rootOrgId = '' + // tslint:disable-next-line + if (typeof req.session != "undefined" && typeof req.session.rootOrgId != "undefined") { + // tslint:disable-next-line + rootOrgId = req.session.rootOrgId + } const response = await axios.get(API_END_POINTS.autoenrollment, { ...axiosRequestConfig, headers: { @@ -153,6 +159,7 @@ cohortsApi.get('/user/autoenrollment/:courseId', async (req, res) => { courseId, rootOrg: rootOrgValue, userUUID: wid, + 'x-authenticated-user-orgid': rootOrgId, // tslint:disable-next-line: no-duplicate-string 'x-authenticated-user-token': extractUserToken(req), }, From 00c29ba85edf69afcb5a07c2db9366b67a1097c1 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Wed, 7 Jun 2023 13:07:36 +0530 Subject: [PATCH 368/856] Added user orgId header for cohorts API --- src/protectedApi_v8/cohorts.ts | 7 +++++++ src/utils/whitelistApis.ts | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/protectedApi_v8/cohorts.ts b/src/protectedApi_v8/cohorts.ts index 2f3ef268..fe13f8ed 100644 --- a/src/protectedApi_v8/cohorts.ts +++ b/src/protectedApi_v8/cohorts.ts @@ -46,6 +46,12 @@ cohortsApi.get('/:cohortType/:contentId', async (req, res) => { res.status(400).send(ERROR.ERROR_NO_ORG_DATA) return } + let rootOrgId = '' + // tslint:disable-next-line + if (typeof req.session != "undefined" && typeof req.session.rootOrgId != "undefined") { + // tslint:disable-next-line + rootOrgId = req.session.rootOrgId + } if (cohortType === 'authors') { const host = req.protocol + '://' + req.get('host') const userList = await getAuthorsDetails(host, extractAuthorizationFromRequest(req), contentId) @@ -59,6 +65,7 @@ cohortsApi.get('/:cohortType/:contentId', async (req, res) => { resourceId: contentId, rootOrg: rootOrgValue, userUUID: extractUserIdFromRequest(req), + 'x-authenticated-user-orgid': rootOrgId, // tslint:disable-next-line: no-duplicate-string 'x-authenticated-user-token': extractUserToken(req), }, diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 24927916..9a844a79 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2366,6 +2366,6 @@ export const API_LIST = { '/proxies/v8/workflow/domain/update', '/proxies/v8/workflow/position/search', '/proxies/v8/workflow/org/search', - '/proxies/v8/workflow/domain/search' + '/proxies/v8/workflow/domain/search', ], } From 7e1e0f0ac6bd9524a88cd306711749c85df485cd Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Tue, 20 Jun 2023 19:01:57 +0530 Subject: [PATCH 369/856] Added APIs for blended program --- src/utils/roles.ts | 1 + src/utils/whitelistApis.ts | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/utils/roles.ts b/src/utils/roles.ts index f6a56a05..bef6cb70 100644 --- a/src/utils/roles.ts +++ b/src/utils/roles.ts @@ -14,6 +14,7 @@ export const ROLE = { FRAC_REVIEWER_TWO: 'FRAC_REVIEWER_L2', IFU_MEMBER: 'IFU_MEMBER', MDO_ADMIN: 'MDO_ADMIN', + PROGRAM_COORDINATOR: 'PROGRAM_COORDINATOR', PUBLIC: 'PUBLIC', SPV_ADMIN: 'SPV_ADMIN', STATE_ADMIN: 'STATE_ADMIN', diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 9a844a79..a0b57b5a 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2089,6 +2089,28 @@ export const API_LIST = { ROLE.MDO_ADMIN, ], }, + '/proxies/v8/workflow/blendedprogram/enrol': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/workflow/blendedprogram/search': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PROGRAM_COORDINATOR, + ROLE.MDO_ADMIN, + ], + }, + '/proxies/v8/workflow/blendedprogram/user/search': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -2367,5 +2389,8 @@ export const API_LIST = { '/proxies/v8/workflow/position/search', '/proxies/v8/workflow/org/search', '/proxies/v8/workflow/domain/search', + '/proxies/v8/workflow/blendedprogram/enrol', + '/proxies/v8/workflow/blendedprogram/search', + '/proxies/v8/workflow/blendedprogram/user/search', ], } From a1b6e11253a91157bc698ad7ef116a0fcf7004ab Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Wed, 21 Jun 2023 13:35:24 +0530 Subject: [PATCH 370/856] Added blended program APIs in white list --- src/utils/whitelistApis.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index a0b57b5a..c8c23385 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2111,6 +2111,28 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/workflow/blendedprogram/update': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ROLE.PROGRAM_COORDINATOR, + ], + }, + '/proxies/v8/workflow/blendedprogram/read/mdo/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ], + }, + '/proxies/v8/workflow/blendedprogram/read/pc/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PROGRAM_COORDINATOR, + ], + }, }, URL_PATTERN: [ @@ -2392,5 +2414,8 @@ export const API_LIST = { '/proxies/v8/workflow/blendedprogram/enrol', '/proxies/v8/workflow/blendedprogram/search', '/proxies/v8/workflow/blendedprogram/user/search', + '/proxies/v8/workflow/blendedprogram/update', + '/proxies/v8/workflow/blendedprogram/read/mdo/:id', + '/proxies/v8/workflow/blendedprogram/read/pc/:id', ], } From 76c2edfdaf9e6c16a4d1e42048b34ee719072738 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Fri, 23 Jun 2023 15:08:03 +0530 Subject: [PATCH 371/856] Removed lastName and added newly introduced flags --- src/protectedApi_v8/user/profile-details.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/protectedApi_v8/user/profile-details.ts b/src/protectedApi_v8/user/profile-details.ts index bcaf26dd..a3b618ac 100644 --- a/src/protectedApi_v8/user/profile-details.ts +++ b/src/protectedApi_v8/user/profile-details.ts @@ -228,7 +228,6 @@ profileDeatailsApi.post('/createUser', async (req, res) => { const sbemail_ = req.body.personalDetails.email const sbemailVerified_ = true const sbfirstName_ = req.body.personalDetails.firstName - const sblastName_ = req.body.personalDetails.lastName const isEmailRequired = (req.body.personalDetails.isEmailRequired) ? req.body.personalDetails.isEmailRequired : true const searchresponse = await axios({ ...axiosRequestConfig, @@ -264,7 +263,6 @@ profileDeatailsApi.post('/createUser', async (req, res) => { } else { const sbUserProfile: Partial = { channel: sbChannel, email: sbemail_, emailVerified: sbemailVerified_, firstName: sbfirstName_, - lastName: sblastName_, } const response = await axios({ ...axiosRequestConfig, @@ -302,7 +300,7 @@ profileDeatailsApi.post('/createUser', async (req, res) => { username: sbUserReadResponse.data.result.response.userName, // tslint:disable-next-line: object-literal-sort-keys identifier: sbUserReadResponse.data.result.response.identifier, - fullname: sbUserReadResponse.data.result.response.firstName + ' ' + sbUserReadResponse.data.result.response.lastName, + fullname: sbUserReadResponse.data.result.response.firstName, } await axios({ ...axiosRequestConfig, @@ -321,11 +319,12 @@ profileDeatailsApi.post('/createUser', async (req, res) => { employmentDetails: { departmentName: sbChannel, }, + mandatoryFieldsExists: false, personalDetails: { firstname: sbfirstName_, primaryEmail: sbemail_, - surname: sblastName_, }, + verifiedKarmayogi: false, }, userId: sbUserId, } @@ -407,7 +406,7 @@ profileDeatailsApi.post('/createUser', async (req, res) => { } const sbUserProfileResponse: Partial = { - email: sbemail_, firstName: sbfirstName_, lastName: sblastName_, + email: sbemail_, firstName: sbfirstName_, userId: sbUserId, userOrgId: sbUserOrgId, } From e5c83980ed2c34f8b5f26492271dcc89e08f58e9 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Fri, 7 Jul 2023 15:03:25 +0530 Subject: [PATCH 372/856] Updated role for Adding master data API --- src/utils/whitelistApis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index c8c23385..75746e17 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2031,7 +2031,7 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PUBLIC, + ROLE.SPV_ADMIN, ], }, '/proxies/v8/user/v1/bulkuser/download/:id' : { From a344bd7eecca76c4cbc2b457840e9d1d51a38f4d Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Wed, 12 Jul 2023 10:34:10 +0530 Subject: [PATCH 373/856] Added V4 APIs w.r.t. assessment --- src/protectedApi_v8/user/evaluate.ts | 39 ++++++++++++++++++++++++++++ src/proxies_v8/proxies_v8.ts | 4 +-- src/utils/whitelistApis.ts | 16 ++++++++++++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/protectedApi_v8/user/evaluate.ts b/src/protectedApi_v8/user/evaluate.ts index 142ed573..f5feab71 100644 --- a/src/protectedApi_v8/user/evaluate.ts +++ b/src/protectedApi_v8/user/evaluate.ts @@ -9,6 +9,7 @@ const GENERAL_ERR_MSG = 'Failed due to unknown reason' const API_END_POINTS = { assessmentSubmitV2: `${CONSTANTS.KONG_API_BASE}/v2/user`, assessmentSubmitV3: `${CONSTANTS.KONG_API_BASE}/v3/user`, + assessmentSubmitV4: `${CONSTANTS.KONG_API_BASE}/v4/user`, iapSubmitAssessment: `${CONSTANTS.SB_EXT_API_BASE_2}/v3/iap-assessment`, postAssessment: `${CONSTANTS.POST_ASSESSMENT_BASE}/lmsapi/v1/post_assessment`, } @@ -34,6 +35,7 @@ evaluateApi.post('/assessment/submit/v2', async (req, res) => { Authorization: CONSTANTS.SB_API_KEY, rootOrg, userId, + // tslint:disable-next-line: no-duplicate-string 'x-authenticated-user-token': extractUserToken(req), }, method: 'POST', @@ -62,6 +64,7 @@ evaluateApi.post('/assessment/submit/v3', async (req, res) => { headers: { Authorization: CONSTANTS.SB_API_KEY, userId, + // tslint:disable-next-line: no-duplicate-string 'x-authenticated-user-token': extractUserToken(req), }, method: 'POST', @@ -133,3 +136,39 @@ evaluateApi.get('/post-assessment/:contentId', async (req, res) => { }) } }) + +evaluateApi.post('/assessment/submit/v4', async (req, res) => { + try { + const userId = extractUserIdFromRequest(req) + const url = `${API_END_POINTS.assessmentSubmitV4}/assessment/submit` + const requestBody = { + ...req.body, + } + let rootOrgId = '' + // tslint:disable-next-line + if (typeof req.session != "undefined" && typeof req.session.rootOrgId != "undefined") { + // tslint:disable-next-line + rootOrgId = req.session.rootOrgId + } + const response = await axios({ + ...axiosRequestConfig, + data: requestBody, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + userId, + 'x-authenticated-user-orgid': rootOrgId, + // tslint:disable-next-line: no-duplicate-string + 'x-authenticated-user-token': extractUserToken(req), + }, + method: 'POST', + url, + }) + res.status(response.status).send(response.data) + } catch (err) { + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: GENERAL_ERR_MSG, + } + ) + } +}) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index f5222c63..28d9f0b8 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -487,12 +487,12 @@ proxiesV8.use('/discussion/*', proxiesV8.use('/assessment/read/*', // tslint:disable-next-line: max-line-length - proxyAssessmentRead(express.Router(), `${CONSTANTS.KONG_API_BASE}` + '/player/questionset/v1/hierarchy') + proxyAssessmentRead(express.Router(), `${CONSTANTS.KONG_API_BASE}` + '/player/questionset/v4/hierarchy') ) proxiesV8.use('/question/read', // tslint:disable-next-line: max-line-length - proxyQuestionRead(express.Router(), `${CONSTANTS.KONG_API_BASE}` + '/player/question/v1/list') + proxyQuestionRead(express.Router(), `${CONSTANTS.KONG_API_BASE}` + '/player/question/v4/list') ) proxiesV8.use('/cbp/question/list', diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 9a844a79..6a98ca21 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2089,6 +2089,20 @@ export const API_LIST = { ROLE.MDO_ADMIN, ], }, + '/protected/v8/user/evaluate/assessment/submit/v4': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/user/assessment/v4/result': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -2367,5 +2381,7 @@ export const API_LIST = { '/proxies/v8/workflow/position/search', '/proxies/v8/workflow/org/search', '/proxies/v8/workflow/domain/search', + '/protected/v8/user/evaluate/assessment/submit/v4', + '/proxies/v8/user/assessment/v4/result', ], } From 5eb6d61e47b47a21d8c44cbfd91c389c7c23e1dd Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Fri, 14 Jul 2023 17:00:59 +0530 Subject: [PATCH 374/856] Added mdo leader role --- src/utils/roles.ts | 1 + src/utils/whitelistApis.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/utils/roles.ts b/src/utils/roles.ts index bef6cb70..59e0336f 100644 --- a/src/utils/roles.ts +++ b/src/utils/roles.ts @@ -14,6 +14,7 @@ export const ROLE = { FRAC_REVIEWER_TWO: 'FRAC_REVIEWER_L2', IFU_MEMBER: 'IFU_MEMBER', MDO_ADMIN: 'MDO_ADMIN', + MDO_LEADER: 'MDO_LEADER', PROGRAM_COORDINATOR: 'PROGRAM_COORDINATOR', PUBLIC: 'PUBLIC', SPV_ADMIN: 'SPV_ADMIN', diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 75746e17..350d1c14 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -442,6 +442,7 @@ export const API_LIST = { ROLE.MDO_ADMIN, ROLE.SPV_ADMIN, ROLE.STATE_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/data/v1/system/settings/get/notificationPreference': { From 1ab3540f452667e204b3a2feb76da68db854495a Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Fri, 14 Jul 2023 17:50:57 +0530 Subject: [PATCH 375/856] Provided mdo leader for various api access --- src/utils/whitelistApis.ts | 77 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 350d1c14..63fe122d 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -44,6 +44,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.CONTENT_CREATOR, ], }, @@ -109,6 +110,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/user/v1/read/:id': { @@ -274,6 +276,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/dashboard/analytics/getDashboardConfig/Karmayogi/:comp': { @@ -381,6 +384,7 @@ export const API_LIST = { ROLE.CONTENT_CREATOR, ROLE.EDITOR, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/v1/content/retire': { @@ -432,6 +436,7 @@ export const API_LIST = { ROLE.CONTENT_REVIEWER, ROLE.CONTENT_PUBLISHER, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/data/v1/system/settings/get/orgTypeList': { @@ -440,6 +445,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CBC_ADMIN, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.SPV_ADMIN, ROLE.STATE_ADMIN, ROLE.MDO_LEADER, @@ -464,6 +470,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.SPV_ADMIN, ROLE.CBP_ADMIN, ROLE.STATE_ADMIN, @@ -483,6 +490,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CBC_ADMIN, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.SPV_ADMIN, ROLE.STATE_ADMIN, ], @@ -493,6 +501,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CBC_ADMIN, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.SPV_ADMIN, ROLE.STATE_ADMIN, ], @@ -503,6 +512,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CBC_ADMIN, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.SPV_ADMIN, ROLE.STATE_ADMIN, ], @@ -513,6 +523,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CBC_ADMIN, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.SPV_ADMIN, ROLE.STATE_ADMIN, ], @@ -522,6 +533,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.SPV_ADMIN, ROLE.CBP_ADMIN, ROLE.STATE_ADMIN, @@ -533,6 +545,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.SPV_ADMIN, ROLE.CBP_ADMIN, ROLE.STATE_ADMIN, @@ -545,6 +558,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CBC_ADMIN, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.SPV_ADMIN, ROLE.STATE_ADMIN, ], @@ -562,6 +576,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CBC_ADMIN, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.SPV_ADMIN, ROLE.STATE_ADMIN, ], @@ -656,6 +671,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CBC_ADMIN, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.SPV_ADMIN, ROLE.STATE_ADMIN, ], @@ -666,6 +682,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CBC_ADMIN, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.SPV_ADMIN, ], }, @@ -827,6 +844,7 @@ export const API_LIST = { ROLE.CBC_ADMIN, ROLE.SPV_ADMIN, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/protected/v8/portal/spv/department': { @@ -862,6 +880,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/protected/v8/user/profileDetails/createUser': { @@ -869,6 +888,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.SPV_ADMIN, ROLE.CBP_ADMIN, ROLE.CONTENT_CREATOR, @@ -902,6 +922,8 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CBC_ADMIN, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, + ROLE.MDO_LEADER, ROLE.SPV_ADMIN, ], }, @@ -910,6 +932,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.WAT_MEMBER, ROLE.CBC_ADMIN, ROLE.SPV_ADMIN, @@ -942,6 +965,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.WAT_MEMBER, ROLE.CBC_ADMIN, ROLE.SPV_ADMIN, @@ -953,6 +977,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.WAT_MEMBER, ROLE.CBC_ADMIN, ROLE.SPV_ADMIN, @@ -963,6 +988,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.WAT_MEMBER, ROLE.CBC_ADMIN, ROLE.SPV_ADMIN, @@ -973,12 +999,14 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.WAT_MEMBER, ROLE.CBC_ADMIN, ROLE.SPV_ADMIN, ], SCOPE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/protected/v8/workallocation/user/autocomplete/:searchTerm': { @@ -1021,6 +1049,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.WAT_MEMBER, ROLE.CBC_ADMIN, ROLE.SPV_ADMIN, @@ -1031,6 +1060,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.WAT_MEMBER, ROLE.CBC_ADMIN, ROLE.SPV_ADMIN, @@ -1041,6 +1071,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.WAT_MEMBER, ROLE.CBC_ADMIN, ROLE.SPV_ADMIN, @@ -1051,6 +1082,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.WAT_MEMBER, ROLE.CBC_ADMIN, ROLE.SPV_ADMIN, @@ -1257,6 +1289,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/protected/v8/workallocation/v2/update': { @@ -1264,6 +1297,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.WAT_MEMBER, ROLE.CBC_ADMIN, ROLE.SPV_ADMIN, @@ -1358,6 +1392,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.WAT_MEMBER, ROLE.CBC_ADMIN, ROLE.SPV_ADMIN, @@ -1368,6 +1403,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.WAT_MEMBER, ROLE.CBC_ADMIN, ROLE.SPV_ADMIN, @@ -1378,6 +1414,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.WAT_MEMBER, ROLE.CBC_ADMIN, ROLE.SPV_ADMIN, @@ -1388,6 +1425,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.WAT_MEMBER, ROLE.CBC_ADMIN, ROLE.SPV_ADMIN, @@ -1398,6 +1436,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.WAT_MEMBER, ROLE.CBC_ADMIN, ROLE.SPV_ADMIN, @@ -1422,6 +1461,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.WAT_MEMBER, ROLE.CBC_ADMIN, ROLE.SPV_ADMIN, @@ -1542,6 +1582,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/authApi/batch/:key': { @@ -1557,6 +1598,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/authApi/readCert/:certId': { @@ -1576,30 +1618,35 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/staff/position/:orgId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/budget/scheme': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/budget/scheme/:orgId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/budget/scheme/:orgId/:budgetYear': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/orghistory/:orgId/:key': { checksNeeded: [CHECK.ROLE], @@ -1607,6 +1654,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CBC_ADMIN, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.SPV_ADMIN, ], }, @@ -1727,6 +1775,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/storage/delete': { @@ -1734,6 +1783,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/user/v1/extPatch': { @@ -1749,6 +1799,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.PUBLIC, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.CBP_ADMIN, ], }, @@ -1781,12 +1832,14 @@ export const API_LIST = { ROLE.CONTENT_PUBLISHER, ROLE.CONTENT_REVIEWER, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/questionset/v1/create': { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], checksNeeded: [CHECK.ROLE], }, @@ -1796,6 +1849,7 @@ export const API_LIST = { ROLE.CONTENT_PUBLISHER, ROLE.CONTENT_REVIEWER, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], checksNeeded: [CHECK.ROLE], }, @@ -1805,6 +1859,7 @@ export const API_LIST = { ROLE.CONTENT_PUBLISHER, ROLE.CONTENT_REVIEWER, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], checksNeeded: [CHECK.ROLE], }, @@ -1812,6 +1867,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], checksNeeded: [CHECK.ROLE], }, @@ -1819,6 +1875,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CONTENT_PUBLISHER, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], checksNeeded: [CHECK.ROLE], }, @@ -1826,6 +1883,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CONTENT_CREATOR, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], checksNeeded: [CHECK.ROLE], }, @@ -1850,6 +1908,7 @@ export const API_LIST = { ROLE.CONTENT_PUBLISHER, ROLE.CONTENT_REVIEWER, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], checksNeeded: [CHECK.ROLE], }, @@ -1935,6 +1994,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/user/v1/bulkupload/:orgId': { @@ -1942,6 +2002,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/otp/v1/generate': { @@ -1977,6 +2038,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/mdo/content/v3/hierarchy/update': { @@ -1984,6 +2046,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/mdo/content/v3/update/:do_id': { @@ -1991,6 +2054,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/mdo/content/v3/publish/:do_id': { @@ -1998,6 +2062,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/mdo/content/v3/batch/addUser': { @@ -2005,6 +2070,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/mdo/content/v3/batch/removeUser': { @@ -2012,6 +2078,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/user/offensive/data/flag': { @@ -2040,6 +2107,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/workflow/position/update': { @@ -2048,6 +2116,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.SPV_ADMIN, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/workflow/org/update': { @@ -2056,6 +2125,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.SPV_ADMIN, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/workflow/domain/update': { @@ -2064,6 +2134,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.SPV_ADMIN, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/workflow/position/search': { @@ -2072,6 +2143,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.SPV_ADMIN, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/workflow/org/search': { @@ -2080,6 +2152,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.SPV_ADMIN, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/workflow/domain/search': { @@ -2088,6 +2161,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.SPV_ADMIN, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/workflow/blendedprogram/enrol': { @@ -2103,6 +2177,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.PROGRAM_COORDINATOR, ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/workflow/blendedprogram/user/search': { @@ -2117,6 +2192,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.PROGRAM_COORDINATOR, ], }, @@ -2125,6 +2201,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/workflow/blendedprogram/read/pc/:id': { From 350b09477d604fc0bbf4f6abbf9fe7027167c0b3 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Tue, 18 Jul 2023 12:02:21 +0530 Subject: [PATCH 376/856] Providing event publish access for MDO_LEADER --- src/utils/whitelistApis.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 0027ffd1..8463fa99 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -103,6 +103,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/event/v4/create': { From 87f806fdaede339b8f4cb0ac227498548b84f478 Mon Sep 17 00:00:00 2001 From: Rangabashyam P K` <46988344+pkranga@users.noreply.github.com> Date: Thu, 3 Aug 2023 23:04:13 +0530 Subject: [PATCH 377/856] File Upload change from KMW to Content Service --- src/proxies_v8/proxies_v8.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 28d9f0b8..ede12765 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -44,7 +44,7 @@ proxiesV8.get('/', (_req, res) => { proxiesV8.post('/upload/*', (req, res) => { if (req.files && req.files.data) { - const url = removePrefix('/proxies/v8/upload', req.originalUrl) + const url = removePrefix('/proxies/v8/upload/action', req.originalUrl) const file: UploadedFile = req.files.data as UploadedFile const formData = new FormData() formData.append('file', Buffer.from(file.data), { @@ -63,9 +63,9 @@ proxiesV8.post('/upload/*', (req, res) => { // tslint:disable-next-line: all 'x-authenticated-userid': extractUserIdFromRequest(req), }, - host: 'knowledge-mw-service', + host: 'content-service', path: url, - port: 5000, + port: 9000, }, (err, response) => { From 7e2c916a54164eae1c0c362f14e07ddf65772496 Mon Sep 17 00:00:00 2001 From: Rajesh Kumaravel Date: Wed, 9 Aug 2023 18:28:00 +0530 Subject: [PATCH 378/856] Issue #97820 fix: Update phone number from paricha for User creation workflow --- README.md | 48 ++++++++++++++++++++++++++++++- package.json | 7 +++-- src/publicApi_v8/parichayAuth.ts | 2 +- src/publicApi_v8/ssoUserHelper.ts | 47 ++++++++++++++++++++++++------ 4 files changed, 91 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index fa5749e5..39615559 100644 --- a/README.md +++ b/README.md @@ -1 +1,47 @@ -# sunbird-cb-uiproxy \ No newline at end of file +# sunbird-cb-uiproxy + +CB API proxy server + +---- + +## License + +This project is licensed under the MIT License. See LICENSE for more information. + +## Table of contents + +- [🏗️ Prerequisites](#-prerequisites) +- [📖 Configuration](#-configuration) +- [🌱 Installation](#-installation) +--- + +## 🏗️ Prerequisites + +- Node.js (version 14.X.X) +- Cassandra (Docker) + +## 📖 Configuration + +1. Ensure environment variables are updated in `env.ts` + +2. Docker Cassandra is up and running + +3. Create Keyspace in cassandra + +## 🌱 Installation + +1. Clone the repository: + + ```bash + git clone https://github.com/sunbird-cb/sunbird-cb-uiproxy + +2. Install dependencies: + + ```bash + cd sunbird-cb-uiproxy && npm install + +3. Start the server: + + ```bash + npm run start:igot + diff --git a/package.json b/package.json index 223727b1..d41df3b0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "ui_web-services-v8", "version": "1.0.0", - "description": "", + "description": "SB CB UI PROXY", "main": "index.js", "scripts": { "build": "gulp build", @@ -15,8 +15,11 @@ "start": "node index.js" }, "keywords": [], + "engines": { + "node": ">=14.19.0" + }, "author": "Christopher Fernandes", - "license": "ISC", + "license": "MIT", "dependencies": { "@types/cors": "^2.8.6", "@types/node-xlsx": "^0.15.0", diff --git a/src/publicApi_v8/parichayAuth.ts b/src/publicApi_v8/parichayAuth.ts index 6dfe830f..b1557448 100644 --- a/src/publicApi_v8/parichayAuth.ts +++ b/src/publicApi_v8/parichayAuth.ts @@ -69,7 +69,7 @@ parichayAuth.get('/callback', async (req, res) => { if (!result.userExist) { logInfo('Sunbird User does not exist for email: ' + userDetailResponse.data.loginId) createResult = await createUserWithMailId(userDetailResponse.data.loginId, - userDetailResponse.data.FirstName, userDetailResponse.data.LastName) + userDetailResponse.data.FirstName, userDetailResponse.data.LastName, userDetailResponse.data.MobileNo) if (createResult.errMessage !== '') { result.errMessage = createResult.errMessage } diff --git a/src/publicApi_v8/ssoUserHelper.ts b/src/publicApi_v8/ssoUserHelper.ts index 68694b2a..2e74c9fc 100644 --- a/src/publicApi_v8/ssoUserHelper.ts +++ b/src/publicApi_v8/ssoUserHelper.ts @@ -1,4 +1,5 @@ import axios from 'axios' +import lodash from 'lodash' import { axiosRequestConfig } from '../configs/request.config' import { CONSTANTS } from '../utils/env' import { logError, logInfo } from '../utils/logger' @@ -47,22 +48,50 @@ export async function fetchUserByEmailId(emailId: string) { return Promise.resolve(result) } -export async function createUserWithMailId(emailId: string, firstNameStr: string, lastNameStr: string) { +export async function createUserWithMailId(emailId: string, firstNameStr: string, lastNameStr: string, mobileNoStr = '') { const result = { errMessage : '', userCreated : false, userId: '', } const signUpErr = 'SIGN_UP_ERR-' let statusString = '' + let _reqPayload = { + request: + { + email: emailId, + emailVerified: true, + firstName: firstNameStr.trim() + ' ' + lastNameStr.trim(), + phone: '', + roles: [ 'PUBLIC' ], + }, + } + let _validPhone + try { + // Check mobile number is valid for length + if (mobileNoStr && mobileNoStr.length >= 10) { + // Check phone number starts with `+` and country code belongs to 91 + if (mobileNoStr.charAt(0) === '+' && mobileNoStr.slice(1, 3) === '91' && + mobileNoStr.slice(3, mobileNoStr.length).length === 10) { + _validPhone = mobileNoStr.slice(3, mobileNoStr.length) + } else if (mobileNoStr.slice(0, 2) === '91' && mobileNoStr.slice(2, mobileNoStr.length).length === 10) { + // Check phone number starts with 91 + _validPhone = mobileNoStr.slice(2, mobileNoStr.length) + } else { + // Accept the incoming phone number as it is; since it is not prefixed with `+` or country code + _validPhone = mobileNoStr + } + } + } catch (error) { + logError('ssoUserHelper:createUserWithMailId - Error while validating phone number') + } + // Update the request object + if (_validPhone) { + _reqPayload.request.phone = _validPhone + } else { + _reqPayload = lodash.omit(_reqPayload, 'phone') + } const signUpResponse = await axios({ ...axiosRequestConfig, - data: { - request: - { - email: emailId, - emailVerified: true, - firstName: firstNameStr, - lastName : lastNameStr, - } }, + data: _reqPayload, headers: { Authorization: CONSTANTS.SB_API_KEY, }, From 11777aab02eb502167e4412d1cc75dcf6b6aed65 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 10 Aug 2023 13:14:54 +0530 Subject: [PATCH 379/856] Updated log message for user not found --- src/utils/permissionHelper.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/utils/permissionHelper.ts b/src/utils/permissionHelper.ts index 657b17b3..ec9345c8 100644 --- a/src/utils/permissionHelper.ts +++ b/src/utils/permissionHelper.ts @@ -71,8 +71,8 @@ export const PERMISSION_HELPER = { }, // tslint:disable-next-line: no-any getCurrentUserRoles(reqObj: any, callback: any) { - logInfo('Step 3: getCurrentUserRoles', '------', new Date().toString()) const userId = reqObj.session.userId + logInfo('Step 3: getCurrentUserRoles for user ' + userId, '------', new Date().toString()) const readUrl = `${CONSTANTS.KONG_API_BASE}/user/v2/read/` + userId const options = { headers: { @@ -86,9 +86,16 @@ export const PERMISSION_HELPER = { // tslint:disable-next-line: no-any request.get(options, (err: any, _httpResponse: any, body: any) => { if (body) { - // tslint:disable-next-line: no-console - logInfo('Success user/v2/read::', '------', new Date().toString()) - this.setRolesData(reqObj, callback, body) + // tslint:disable-next-line: no-any + const userData: any = JSON.parse(body) + if (userData.responseCode.toUpperCase() === 'OK') { + logInfo('Success user/v2/read::', '------', new Date().toString()) + this.setRolesData(reqObj, callback, body) + } else { + const errMsg = 'Failed to read the user with Id: ' + userId + 'Error: ' + userData.responseCode + logError(errMsg) + callback(errMsg, null) + } } if (err) { logError('Making axios call to nodeBB ERROR -- ', err, '------', new Date().toString()) From dcead882254c0d915cb63dcc9b5fd75074f33282 Mon Sep 17 00:00:00 2001 From: Sreerag K S <58926794+sreeragksgh@users.noreply.github.com> Date: Thu, 10 Aug 2023 17:00:04 +0530 Subject: [PATCH 380/856] adding countries list API (#162) * adding countries list API --- src/protectedApi_v8/user/profile-registry.ts | 20 ++++++++++++++++++++ src/utils/whitelistApis.ts | 8 ++++++++ 2 files changed, 28 insertions(+) diff --git a/src/protectedApi_v8/user/profile-registry.ts b/src/protectedApi_v8/user/profile-registry.ts index a7842c08..22a26b7a 100644 --- a/src/protectedApi_v8/user/profile-registry.ts +++ b/src/protectedApi_v8/user/profile-registry.ts @@ -14,6 +14,7 @@ const API_END_POINTS = { getUserRegistryById: (userId: string) => `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/search/profile?userId=${userId}`, masterLanguages: `${CONSTANTS.KONG_API_BASE}/masterData/v1/languages`, masterNationalities: `${CONSTANTS.KONG_API_BASE}/masterData/v1/nationalities`, + masterCountries: `${CONSTANTS.KONG_API_BASE}/masterData/v1/countries`, profilePageMetaData: `${CONSTANTS.KONG_API_BASE}/masterData/v1/profilePageMetaData`, searchUserRegistry: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/search/profile`, updateUserRegistry: (userId: string) => `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/update/profile?userId=${userId}`, @@ -173,6 +174,25 @@ profileRegistryApi.get('/getMasterNationalities', async (_req, res) => { } }) +profileRegistryApi.get('/getMasterCountries', async (_req, res) => { + try { + const response = await axios.get(API_END_POINTS.masterCountries, { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + }, + }) + res.send((response.data)) + } catch (err) { + logError(CONNECTION_ERROR, err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: unknown, + } + ) + } +}) + profileRegistryApi.get('/getMasterLanguages', async (_req, res) => { try { const response = await axios.get(API_END_POINTS.masterLanguages, { diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index cba6618d..99fc0b14 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1317,6 +1317,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/user/profileRegistry/getMasterCountries': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/user/profileRegistry/getMasterLanguages': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -2326,6 +2333,7 @@ export const API_LIST = { '/protected/v8/workflowhandler/userWFApplicationFieldsSearch', '/protected/v8/user/details/detailV1', '/protected/v8/user/profileRegistry/getMasterNationalities', + '/protected/v8/user/profileRegistry/getMasterCountries', '/protected/v8/user/profileRegistry/getMasterLanguages', '/protected/v8/user/profileRegistry/getProfilePageMeta', '/protected/v8/user/notifications/settings', From 1d96870d2fb94e94e4506860dd7d316a107a7f0a Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 10 Aug 2023 18:55:37 +0530 Subject: [PATCH 381/856] Added rating API in whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 8463fa99..6e3a5a3d 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2226,6 +2226,13 @@ export const API_LIST = { ROLE.PROGRAM_COORDINATOR, ], }, + '/proxies/v8/ratings/v1/read': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -2512,5 +2519,6 @@ export const API_LIST = { '/proxies/v8/workflow/blendedprogram/update', '/proxies/v8/workflow/blendedprogram/read/mdo/:id', '/proxies/v8/workflow/blendedprogram/read/pc/:id', + '/proxies/v8/ratings/v1/read', ], } From 74a45d66d6890260899bd3a536fa97238e07e830 Mon Sep 17 00:00:00 2001 From: sreeragksgh Date: Fri, 11 Aug 2023 12:18:42 +0530 Subject: [PATCH 382/856] country API changes --- src/protectedApi_v8/user/profile-registry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protectedApi_v8/user/profile-registry.ts b/src/protectedApi_v8/user/profile-registry.ts index 22a26b7a..9c2e20fa 100644 --- a/src/protectedApi_v8/user/profile-registry.ts +++ b/src/protectedApi_v8/user/profile-registry.ts @@ -12,9 +12,9 @@ const API_END_POINTS = { getAllPosition: `${CONSTANTS.FRAC_API_BASE}/frac/getAllNodes?type=POSITION&status=VERIFIED`, getUserRegistry: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/get/profile`, getUserRegistryById: (userId: string) => `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/search/profile?userId=${userId}`, + masterCountries: `${CONSTANTS.KONG_API_BASE}/masterData/v1/countries`, masterLanguages: `${CONSTANTS.KONG_API_BASE}/masterData/v1/languages`, masterNationalities: `${CONSTANTS.KONG_API_BASE}/masterData/v1/nationalities`, - masterCountries: `${CONSTANTS.KONG_API_BASE}/masterData/v1/countries`, profilePageMetaData: `${CONSTANTS.KONG_API_BASE}/masterData/v1/profilePageMetaData`, searchUserRegistry: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/search/profile`, updateUserRegistry: (userId: string) => `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/update/profile?userId=${userId}`, From 20f0b35f1d27ff36e3a3ed088199391426f02151 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 10 Aug 2023 18:55:37 +0530 Subject: [PATCH 383/856] Added rating API in whitelist --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 99fc0b14..0afc07c0 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2154,6 +2154,13 @@ export const API_LIST = { ROLE.PROGRAM_COORDINATOR, ], }, + '/proxies/v8/ratings/v1/read': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -2441,5 +2448,6 @@ export const API_LIST = { '/proxies/v8/workflow/blendedprogram/update', '/proxies/v8/workflow/blendedprogram/read/mdo/:id', '/proxies/v8/workflow/blendedprogram/read/pc/:id', + '/proxies/v8/ratings/v1/read', ], } From ecc992e828c5e5f15236966994c8daa5cb891d09 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Sun, 13 Aug 2023 11:16:59 +0530 Subject: [PATCH 384/856] Added condition before setting nodebb related headers --- src/utils/proxyCreator.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 5aba37ae..29e3ac36 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -33,7 +33,9 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { } proxyReq.setHeader('x-authenticated-user-orgname', channel) proxyReq.setHeader('x-authenticated-user-channel', channel) - proxyReq.setHeader('x-authenticated-user-nodebb-uid', req.session.uid) + if (req.session.hasOwnProperty('uid')) { + proxyReq.setHeader('x-authenticated-user-nodebb-uid', req.session.uid) + } // condition has been added to set the session in nodebb req header /* tslint:disable-next-line */ From d62f9226962f45b6f3d45678166eaf11dd2007df Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Mon, 14 Aug 2023 14:40:03 +0530 Subject: [PATCH 385/856] Updated ratings POST API version as v2 --- src/utils/whitelistApis.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 0afc07c0..6f5abb15 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2154,7 +2154,7 @@ export const API_LIST = { ROLE.PROGRAM_COORDINATOR, ], }, - '/proxies/v8/ratings/v1/read': { + '/proxies/v8/ratings/v2/read': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ @@ -2448,6 +2448,6 @@ export const API_LIST = { '/proxies/v8/workflow/blendedprogram/update', '/proxies/v8/workflow/blendedprogram/read/mdo/:id', '/proxies/v8/workflow/blendedprogram/read/pc/:id', - '/proxies/v8/ratings/v1/read', + '/proxies/v8/ratings/v2/read', ], } From 5ff107b056add2c43d06933eb1af2ed532ab6eec Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Thu, 10 Aug 2023 18:55:37 +0530 Subject: [PATCH 386/856] Added ratings V2 API --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 6a98ca21..1c3356ec 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2103,6 +2103,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/ratings/v2/read': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -2383,5 +2390,6 @@ export const API_LIST = { '/proxies/v8/workflow/domain/search', '/protected/v8/user/evaluate/assessment/submit/v4', '/proxies/v8/user/assessment/v4/result', + '/proxies/v8/ratings/v2/read', ], } From f203a8274639afb2c7934c6380d4c4929621405f Mon Sep 17 00:00:00 2001 From: Sreerag K S <58926794+sreeragksgh@users.noreply.github.com> Date: Thu, 10 Aug 2023 17:00:04 +0530 Subject: [PATCH 387/856] adding countries list API (#162) * adding countries list API --- src/protectedApi_v8/user/profile-registry.ts | 20 ++++++++++++++++++++ src/utils/whitelistApis.ts | 8 ++++++++ 2 files changed, 28 insertions(+) diff --git a/src/protectedApi_v8/user/profile-registry.ts b/src/protectedApi_v8/user/profile-registry.ts index a7842c08..22a26b7a 100644 --- a/src/protectedApi_v8/user/profile-registry.ts +++ b/src/protectedApi_v8/user/profile-registry.ts @@ -14,6 +14,7 @@ const API_END_POINTS = { getUserRegistryById: (userId: string) => `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/search/profile?userId=${userId}`, masterLanguages: `${CONSTANTS.KONG_API_BASE}/masterData/v1/languages`, masterNationalities: `${CONSTANTS.KONG_API_BASE}/masterData/v1/nationalities`, + masterCountries: `${CONSTANTS.KONG_API_BASE}/masterData/v1/countries`, profilePageMetaData: `${CONSTANTS.KONG_API_BASE}/masterData/v1/profilePageMetaData`, searchUserRegistry: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/search/profile`, updateUserRegistry: (userId: string) => `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/update/profile?userId=${userId}`, @@ -173,6 +174,25 @@ profileRegistryApi.get('/getMasterNationalities', async (_req, res) => { } }) +profileRegistryApi.get('/getMasterCountries', async (_req, res) => { + try { + const response = await axios.get(API_END_POINTS.masterCountries, { + ...axiosRequestConfig, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + }, + }) + res.send((response.data)) + } catch (err) { + logError(CONNECTION_ERROR, err) + res.status((err && err.response && err.response.status) || 500).send( + (err && err.response && err.response.data) || { + error: unknown, + } + ) + } +}) + profileRegistryApi.get('/getMasterLanguages', async (_req, res) => { try { const response = await axios.get(API_END_POINTS.masterLanguages, { diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 1c3356ec..01b2ce51 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1317,6 +1317,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/user/profileRegistry/getMasterCountries': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/user/profileRegistry/getMasterLanguages': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -2289,6 +2296,7 @@ export const API_LIST = { '/protected/v8/workflowhandler/userWFApplicationFieldsSearch', '/protected/v8/user/details/detailV1', '/protected/v8/user/profileRegistry/getMasterNationalities', + '/protected/v8/user/profileRegistry/getMasterCountries', '/protected/v8/user/profileRegistry/getMasterLanguages', '/protected/v8/user/profileRegistry/getProfilePageMeta', '/protected/v8/user/notifications/settings', From 4cc0270d9c12b6b473f6dda63954fdf3f3ca030e Mon Sep 17 00:00:00 2001 From: sreeragksgh Date: Fri, 11 Aug 2023 12:18:42 +0530 Subject: [PATCH 388/856] country API changes --- src/protectedApi_v8/user/profile-registry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/protectedApi_v8/user/profile-registry.ts b/src/protectedApi_v8/user/profile-registry.ts index 22a26b7a..9c2e20fa 100644 --- a/src/protectedApi_v8/user/profile-registry.ts +++ b/src/protectedApi_v8/user/profile-registry.ts @@ -12,9 +12,9 @@ const API_END_POINTS = { getAllPosition: `${CONSTANTS.FRAC_API_BASE}/frac/getAllNodes?type=POSITION&status=VERIFIED`, getUserRegistry: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/get/profile`, getUserRegistryById: (userId: string) => `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/search/profile?userId=${userId}`, + masterCountries: `${CONSTANTS.KONG_API_BASE}/masterData/v1/countries`, masterLanguages: `${CONSTANTS.KONG_API_BASE}/masterData/v1/languages`, masterNationalities: `${CONSTANTS.KONG_API_BASE}/masterData/v1/nationalities`, - masterCountries: `${CONSTANTS.KONG_API_BASE}/masterData/v1/countries`, profilePageMetaData: `${CONSTANTS.KONG_API_BASE}/masterData/v1/profilePageMetaData`, searchUserRegistry: `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/search/profile`, updateUserRegistry: (userId: string) => `${CONSTANTS.NETWORK_HUB_SERVICE_BACKEND}/v1/user/update/profile?userId=${userId}`, From af41c4f3d6cca5fad45ee8749b2a4179e62d0a82 Mon Sep 17 00:00:00 2001 From: Rangabashyam P K` <46988344+pkranga@users.noreply.github.com> Date: Thu, 3 Aug 2023 23:04:13 +0530 Subject: [PATCH 389/856] File Upload change from KMW to Content Service --- src/proxies_v8/proxies_v8.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index 28d9f0b8..ede12765 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -44,7 +44,7 @@ proxiesV8.get('/', (_req, res) => { proxiesV8.post('/upload/*', (req, res) => { if (req.files && req.files.data) { - const url = removePrefix('/proxies/v8/upload', req.originalUrl) + const url = removePrefix('/proxies/v8/upload/action', req.originalUrl) const file: UploadedFile = req.files.data as UploadedFile const formData = new FormData() formData.append('file', Buffer.from(file.data), { @@ -63,9 +63,9 @@ proxiesV8.post('/upload/*', (req, res) => { // tslint:disable-next-line: all 'x-authenticated-userid': extractUserIdFromRequest(req), }, - host: 'knowledge-mw-service', + host: 'content-service', path: url, - port: 5000, + port: 9000, }, (err, response) => { From 2925928c2632178422691505edfc1a7f5a972fd1 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Tue, 22 Aug 2023 00:16:36 +0530 Subject: [PATCH 390/856] Accepting error for NodeBB user create API --- src/utils/permissionHelper.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/permissionHelper.ts b/src/utils/permissionHelper.ts index 657b17b3..c76fd612 100644 --- a/src/utils/permissionHelper.ts +++ b/src/utils/permissionHelper.ts @@ -61,7 +61,7 @@ export const PERMISSION_HELPER = { reqObj.session.save((error: any) => { if (error) { logError('reqObj.session.save error -- ', error, '------', new Date().toString()) - callback(error, null) + callback(null, null) } else { // tslint:disable-next-line: no-console console.log(`setNodeBBUID::Success of save -- reqObj.session ${new Date()}--- `) @@ -125,7 +125,7 @@ export const PERMISSION_HELPER = { } catch (err) { // tslint:disable-next-line: no-console console.log('Making axios call to nodeBB ERROR -- ', err, '------', new Date().toString()) - callback(err, null) + callback(null, null) } }, } From 61dcf2a3485530a0a04d97001c0644aff31aa2fc Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Tue, 22 Aug 2023 01:19:46 +0530 Subject: [PATCH 391/856] Handling undefined uid value in session --- src/utils/proxyCreator.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/utils/proxyCreator.ts b/src/utils/proxyCreator.ts index 5aba37ae..8192b6d4 100644 --- a/src/utils/proxyCreator.ts +++ b/src/utils/proxyCreator.ts @@ -33,13 +33,15 @@ proxy.on('proxyReq', (proxyReq: any, req: any, _res: any, _options: any) => { } proxyReq.setHeader('x-authenticated-user-orgname', channel) proxyReq.setHeader('x-authenticated-user-channel', channel) - proxyReq.setHeader('x-authenticated-user-nodebb-uid', req.session.uid) + if (req.session.hasOwnProperty('uid')) { + proxyReq.setHeader('x-authenticated-user-nodebb-uid', req.session.uid) + } // condition has been added to set the session in nodebb req header /* tslint:disable-next-line */ if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create') && req.session) { - if (req.body) { + if (req.body && req.session.hasOwnProperty('uid')) { req.body._uid = req.session.uid } // tslint:disable-next-line: no-console @@ -161,10 +163,12 @@ export function proxyCreatorSunbird(route: Router, targetUrl: string, _timeout = } if (req.originalUrl.includes('/discussion') && !req.originalUrl.includes('/discussion/user/v1/create') && req.session) { - if (req.originalUrl.includes('?')) { - url = `${url}&_uid=${req.session.uid}` - } else { - url = `${url}?_uid=${req.session.uid}` + if (req.session.hasOwnProperty('uid')) { + if (req.originalUrl.includes('?')) { + url = `${url}&_uid=${req.session.uid}` + } else { + url = `${url}?_uid=${req.session.uid}` + } } if (req.originalUrl.includes('/discussion/v2/topics')) { req.body.email = extractUserEmailFromRequest(req) From 13031e9f782b7e9c51ac299b2cc2e3fc653610dd Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Thu, 24 Aug 2023 19:20:24 +0530 Subject: [PATCH 392/856] Added workflow API for BlendedProgram Stats (#172) --- src/utils/whitelistApis.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 6f5abb15..43d9f006 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2161,6 +2161,14 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/workflow/blendedprogram/v1/stats': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.CBP_ADMIN, + ROLE.PROGRAM_COORDINATOR, + ], + }, }, URL_PATTERN: [ @@ -2449,5 +2457,6 @@ export const API_LIST = { '/proxies/v8/workflow/blendedprogram/read/mdo/:id', '/proxies/v8/workflow/blendedprogram/read/pc/:id', '/proxies/v8/ratings/v2/read', + '/proxies/v8/workflow/blendedprogram/v1/stats', ], } From c28600cc3c1b7e7fa454e612cb04f146f9d176b5 Mon Sep 17 00:00:00 2001 From: Sahil-tarento <140611066+Sahil-tarento@users.noreply.github.com> Date: Fri, 25 Aug 2023 14:53:55 +0530 Subject: [PATCH 393/856] Dev #99624 Storage service API creation (#173) * Update whitelistApis.ts --- src/utils/whitelistApis.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index d6b35da6..f86302e0 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2248,6 +2248,14 @@ export const API_LIST = { ROLE.PROGRAM_COORDINATOR, ], }, + '/proxies/v8/storage/v1/report/:reportType/:date/:orgId/:fileName': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ROLE.CBP_ADMIN, + ], + }, }, URL_PATTERN: [ @@ -2537,5 +2545,6 @@ export const API_LIST = { '/proxies/v8/workflow/blendedprogram/read/pc/:id', '/proxies/v8/ratings/v2/read', '/proxies/v8/workflow/blendedprogram/v1/stats', + '/proxies/v8/storage/v1/report/:reportType/:date/:orgId/:fileName', ], } From 87536d70920d3e892f079b108921b841cc214c2c Mon Sep 17 00:00:00 2001 From: Karthikeyan Rajendran <70887864+karthik-tarento@users.noreply.github.com> Date: Thu, 31 Aug 2023 20:12:42 +0530 Subject: [PATCH 394/856] 4.8.4 create user fix (#177) * Fix for SPV create user issue w.r.t nodebb * Added necessary configs --- src/models/user.model.ts | 1 + src/protectedApi_v8/user/profile-details.ts | 261 ++++++++++---------- src/utils/env.ts | 16 +- 3 files changed, 136 insertions(+), 142 deletions(-) diff --git a/src/models/user.model.ts b/src/models/user.model.ts index 966bd3d3..d684e021 100644 --- a/src/models/user.model.ts +++ b/src/models/user.model.ts @@ -133,6 +133,7 @@ export interface ISBUser { channel: string userName: string password: string + roles: string[] } export interface IPersonalDetails { diff --git a/src/protectedApi_v8/user/profile-details.ts b/src/protectedApi_v8/user/profile-details.ts index a3b618ac..31ef1580 100644 --- a/src/protectedApi_v8/user/profile-details.ts +++ b/src/protectedApi_v8/user/profile-details.ts @@ -8,6 +8,7 @@ import { logError, logInfo } from '../../utils/logger' import { ERROR } from '../../utils/message' import { extractUserIdFromRequest, extractUserToken } from '../../utils/requestExtract' +const _ = require('lodash') const uuidv1 = require('uuid/v1') const dateFormat = require('dateformat') @@ -225,87 +226,75 @@ profileDeatailsApi.post('/createUser', async (req, res) => { return } let statusString = '' + let errMsg = '' const sbemail_ = req.body.personalDetails.email const sbemailVerified_ = true const sbfirstName_ = req.body.personalDetails.firstName const isEmailRequired = (req.body.personalDetails.isEmailRequired) ? req.body.personalDetails.isEmailRequired : true - const searchresponse = await axios({ + const userRoles = (req.body.personalDetails.roles) ? req.body.personalDetails.roles : undefined + let sbUserProfile: Partial = { + channel: sbChannel, email: sbemail_, emailVerified: sbemailVerified_, + firstName: sbfirstName_, roles: userRoles, + } + if (userRoles === undefined) { + sbUserProfile = _.omit(sbUserProfile, 'roles') + } + let userCreateResponse + try { + userCreateResponse = await axios({ ...axiosRequestConfig, - data: { request: { query: '', filters: { email: sbemail_.toLowerCase() } } }, - headers: { + data: { request: sbUserProfile }, + headers: { Authorization: CONSTANTS.SB_API_KEY, // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req), }, method: 'POST', - url: API_END_POINTS.kongSearchUser, + url: API_END_POINTS.kongCreateUser, }) - if (searchresponse.data.result.response.count > 0) { - res.status(400).send( - { - id: 'api.error.createUser', - ver: '1.0', - // tslint:disable-next-line: object-literal-sort-keys - ts: dateFormat(new Date(), 'yyyy-mm-dd HH:MM:ss:lo'), - params: - { - resmsgid: uuidv1(), - // tslint:disable-next-line: object-literal-sort-keys - msgid: null, - status: 'failed', - err: 'USR_EMAIL_EXISTS', - errmsg: emailAdressExist, - }, - responseCode: 'USR_EMAIL_EXISTS', - result: {}, - }) - return - } else { - const sbUserProfile: Partial = { - channel: sbChannel, email: sbemail_, emailVerified: sbemailVerified_, firstName: sbfirstName_, + } catch (createErr) { + errMsg = createErr.response.data.params.errmsg + let errStatus = 500 + if (createErr.response.data.responseCode === 'CLIENT_ERROR') { + errStatus = 400 } - const response = await axios({ + logError ('Failed to create User, error msg : ' + errMsg) + res.status(errStatus).send(createErr.response.data) + } + if (userCreateResponse && userCreateResponse.data && userCreateResponse.data.responseCode === 'CLIENT_ERROR') { + errMsg = userCreateResponse.data.params ? userCreateResponse.data.params.errmsg : failedToCreateUser + res.status(400).send(userCreateResponse.data) + return + } else if (userCreateResponse) { + const sbUserId = userCreateResponse.data.result.userId + const sbUserReadResponse = await axios({ ...axiosRequestConfig, - data: { request: sbUserProfile }, - headers: { + headers: { Authorization: CONSTANTS.SB_API_KEY, // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req), }, - method: 'POST', - url: API_END_POINTS.kongCreateUser, + method: 'GET', + url: API_END_POINTS.kongUserRead(sbUserId), }) - if (response.data.responseCode === 'CLIENT_ERROR') { - res.status(400).send(failedToCreateUser) + statusString = sbUserReadResponse.data.params.status + if (statusString.toUpperCase() !== 'SUCCESS') { + res.status(500).send(failedToReadUser) return - } else { - const sbUserId = response.data.result.userId - const sbUserReadResponse = await axios({ - ...axiosRequestConfig, - headers: { - Authorization: CONSTANTS.SB_API_KEY, - // tslint:disable-next-line: all - 'x-authenticated-user-token': extractUserToken(req), - }, - method: 'GET', - url: API_END_POINTS.kongUserRead(sbUserId), - }) - statusString = sbUserReadResponse.data.params.status - if (statusString.toUpperCase() !== 'SUCCESS') { - res.status(500).send(failedToReadUser) - return - } + } + if (CONSTANTS.PORTAL_CREATE_NODEBB_USER === 'true') { + try { // tslint:disable-next-line: no-commented-code const nodebbPayload = { - username: sbUserReadResponse.data.result.response.userName, - // tslint:disable-next-line: object-literal-sort-keys - identifier: sbUserReadResponse.data.result.response.identifier, - fullname: sbUserReadResponse.data.result.response.firstName, + username: sbUserReadResponse.data.result.response.userName, + // tslint:disable-next-line: object-literal-sort-keys + identifier: sbUserReadResponse.data.result.response.identifier, + fullname: sbUserReadResponse.data.result.response.firstName, } await axios({ ...axiosRequestConfig, data: { request: nodebbPayload }, - headers: { + headers: { Authorization: CONSTANTS.SB_API_KEY, // tslint:disable-next-line: all 'x-authenticated-user-token': extractUserToken(req), @@ -313,108 +302,112 @@ profileDeatailsApi.post('/createUser', async (req, res) => { method: 'POST', url: API_END_POINTS.createNodeBBUser, }) - const sbUserOrgId = sbUserReadResponse.data.result.response.rootOrgId - const sbProfileUpdateReq = { - profileDetails: { - employmentDetails: { - departmentName: sbChannel, - }, - mandatoryFieldsExists: false, - personalDetails: { - firstname: sbfirstName_, - primaryEmail: sbemail_, - }, - verifiedKarmayogi: false, + } catch (nodeBBerr) { + logError('Failed to create NodeBB account for user: ' + sbUserId) + } + } + const sbUserOrgId = sbUserReadResponse.data.result.response.rootOrgId + const sbProfileUpdateReq = { + profileDetails: { + employmentDetails: { + departmentName: sbChannel, }, - userId: sbUserId, + mandatoryFieldsExists: false, + personalDetails: { + firstname: sbfirstName_, + primaryEmail: sbemail_, + }, + verifiedKarmayogi: false, + }, + userId: sbUserId, + } + if (req.body.personalDetails.designation) { + const arrDesignation = [] + const objDesignation = { + designation: (req.body.personalDetails.designation) ? req.body.personalDetails.designation : '', } - if (req.body.personalDetails.designation) { - const arrDesignation = [] - const objDesignation = { - designation: (req.body.personalDetails.designation) ? req.body.personalDetails.designation : '', - } - arrDesignation.push(objDesignation) - const profDetailsPropertyName = 'professionalDetails' - sbProfileUpdateReq[profDetailsPropertyName] = arrDesignation + arrDesignation.push(objDesignation) + const profDetailsPropertyName = 'professionalDetails' + sbProfileUpdateReq[profDetailsPropertyName] = arrDesignation + } + + const sbUserProfileUpdateResp = await axios({ + ...axiosRequestConfig, + data: { request: sbProfileUpdateReq }, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + }, + method: 'PATCH', + url: API_END_POINTS.kongUpdateUser, + }) + if (sbUserProfileUpdateResp.data.responseCode === 'CLIENT_ERROR') { + errMsg = sbUserProfileUpdateResp.data.params ? sbUserProfileUpdateResp.data.params.errmsg : failedToUpdateUser + res.status(400).send(errMsg) + return + } + if (isEmailRequired) { + const passwordResetRequest = { + key: 'email', + type: 'email', + userId: sbUserId, } - const sbUserProfileUpdateResp = await axios({ + // logInfo('Sending Password reset request -> ' + JSON.stringify(passwordResetRequest)) + const passwordResetResponse = await axios({ ...axiosRequestConfig, - data: { request: sbProfileUpdateReq }, + data: { request: passwordResetRequest }, headers: { Authorization: CONSTANTS.SB_API_KEY, }, - method: 'PATCH', - url: API_END_POINTS.kongUpdateUser, + method: 'POST', + url: API_END_POINTS.kongUserResetPassword, }) - if (sbUserProfileUpdateResp.data.responseCode === 'CLIENT_ERROR') { - res.status(400).send(failedToUpdateUser) - return - } - if (isEmailRequired) { - const passwordResetRequest = { - key: 'email', - type: 'email', - userId: sbUserId, + // logInfo('Received response from password reset -> ' + JSON.stringify(passwordResetResponse.data)) + statusString = passwordResetResponse.data.params.status + if (statusString.toUpperCase() === 'SUCCESS') { + const welcomeMailRequest = { + allowedLoging: 'You can use your email to Login', + body: 'Hello', + emailTemplateType: 'iGotWelcome_v3', + firstName: sbUserProfile.firstName, + link: passwordResetResponse.data.result.link, + mode: 'email', + orgName: sbChannel, + recipientEmails: [ sbemail_ ], + setPasswordLink: true, + subject: 'Welcome to iGOT Karmayogi... Activate your account now!', + welcomeMessage: 'Hello', } - logInfo('Sending Password reset request -> ' + passwordResetRequest) - const passwordResetResponse = await axios({ + const welcomeMailResponse = await axios({ ...axiosRequestConfig, - data: { request: passwordResetRequest }, + data: { request: welcomeMailRequest }, headers: { Authorization: CONSTANTS.SB_API_KEY, }, method: 'POST', - url: API_END_POINTS.kongUserResetPassword, + url: API_END_POINTS.kongSendWelcomeEmail, }) - logInfo('Received response from password reset -> ' + passwordResetResponse) - statusString = passwordResetResponse.data.params.status - if (statusString.toUpperCase() === 'SUCCESS') { - const welcomeMailRequest = { - allowedLoging: 'You can use your email to Login', - body: 'Hello', - emailTemplateType: 'iGotWelcome_v3', - firstName: sbUserProfile.firstName, - link: passwordResetResponse.data.result.link, - mode: 'email', - orgName: sbChannel, - recipientEmails: [ sbemail_ ], - setPasswordLink: true, - subject: 'Welcome to iGOT Karmayogi... Activate your account now!', - welcomeMessage: 'Hello', - } - - const welcomeMailResponse = await axios({ - ...axiosRequestConfig, - data: { request: welcomeMailRequest }, - headers: { - Authorization: CONSTANTS.SB_API_KEY, - }, - method: 'POST', - url: API_END_POINTS.kongSendWelcomeEmail, - }) - statusString = welcomeMailResponse.data.params.status - if (statusString.toUpperCase() !== 'SUCCESS') { - res.status(500).send('Failed to send Welcome Email.') - return - } - } else { - res.status(500).send('Failed to reset the password for user.') + statusString = welcomeMailResponse.data.params.status + if (statusString.toUpperCase() !== 'SUCCESS') { + res.status(500).send('Failed to send Welcome Email.') return } + } else { + res.status(500).send('Failed to reset the password for user.') + return } + } - const sbUserProfileResponse: Partial = { - email: sbemail_, firstName: sbfirstName_, - userId: sbUserId, - userOrgId: sbUserOrgId, - } - res.send(sbUserProfileResponse) + const sbUserProfileResponse: Partial = { + email: sbemail_, firstName: sbfirstName_, + userId: sbUserId, + userOrgId: sbUserOrgId, } + res.send(sbUserProfileResponse) } } catch (err) { - logError(createUserFailed, err) + logError('test1 --> ' + createUserFailed, err) res.status((err && err.response && err.response.status) || 500).send(err) } }) diff --git a/src/utils/env.ts b/src/utils/env.ts index b26d23b5..d74d8218 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -56,7 +56,7 @@ export const CONSTANTS = { KHUB_GRAPH_DATA: env.KHUB_GRAPH_DATA || 'http://localhost:3016', KHUB_SEARCH_BASE: env.KHUB_SEARCH_BASE || 'http://localhost:3014', KNOWLEDGE_MW_API_BASE: env.KNOWLEDGE_MW_API_BASE || 'http://knowledge-mw-service:5000', - KONG_API_BASE: env.KONG_API_BASE || 'https://igot-dev.in/api', + KONG_API_BASE: env.KONG_API_BASE || 'https://portal.karmayogi.nic.in/api', PORTAL_CASSANDRA_CONSISTENCY_LEVEL: env.PORTAL_CASSANDRA_CONSISTENCY_LEVEL || 'one', POST_ASSESSMENT_BASE: env.POST_ASSESSMENT_BASE || 'http://localhost.com', POST_ASSESSMENT_CLIENT_ID: env.POST_ASSESSMENT_CLIENT_ID || '', @@ -68,7 +68,7 @@ export const CONSTANTS = { LIKE_API_BASE: env.LIKE_API_BASE || env.SB_EXT_API_BASE_2, MULTI_TENANT_KEYCLOAK: env.MULTI_TENANT_KEYCLOAK || - 'igot,https://igot-dev.in/auth,sunbird', + 'igot,https://portal.karmayogi.nic.in/auth,sunbird', NAVIGATOR_JSON_HOST: env.NAVIGATOR_JSON_HOST || 'http://localhost:3007/web-hosted/navigator/json', NODE_API_BASE: env.NODE_API_BASE || 'http://localhost:5001', @@ -139,10 +139,10 @@ export const CONSTANTS = { DISCUSSION_CATEGORY_LIST: env.DISCUSSION_CATEGORY_LIST || 'cid[]=5&cid[]=6&cid[]=8&cid[]=9&cid[]=10&cid[]=11&cid[]=12&cid[]=13', WORKFLOW_HANDLER_SERVICE_API_BASE: env.WORKFLOW_HANDLER_SERVICE_API_BASE || 'http://localhost:5099', SUNBIRD_PROXY_URL: env.SUNBIRD_PROXY_URL || 'https://igot-sunbird.idc.tarento.com/apis/proxies/v8/action', - SUNBIRD_PROXY_API_BASE: env.SUNBIRD_PROXY_API_BASE || 'https://igot-dev.in/api', + SUNBIRD_PROXY_API_BASE: env.SUNBIRD_PROXY_API_BASE || 'https://portal.karmayogi.nic.in/api', SCORING_SERVICE_API_BASE: env.SCORING_SERVICE_API_BASE || 'http://localhost:7014', // tslint:disable-next-line:max-line-length - SB_API_KEY: env.SB_API_KEY || 'bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJRekw4VVA1dUtqUFdaZVpMd1ZtTFJvNHdqWTg2a2FrcSJ9.TPjV0xLacSbp3FbJ7XeqHoKFN35Rl4YHx3DZNN9pm0o', + SB_API_KEY: env.SB_API_KEY || 'bearer apiKey', LEARNER_SERVICE_API_BASE: env.LEARNER_SERVICE_API_BASE || 'http://learner-service:9000', X_Channel_Id: env.X_CHANNEL_ID || '0131397178949058560', NOTIFICATION_SERVIC_API_BASE: env.NOTIFICATION_SERVIC_API_BASE || 'http://notification-service:9000', @@ -159,15 +159,15 @@ export const CONSTANTS = { // tslint:disable-next-line: max-line-length CERT_AUTH_TOKEN: 'bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJRekw4VVA1dUtqUFdaZVpMd1ZtTFJvNHdqWTg2a2FrcSJ9.TPjV0xLacSbp3FbJ7XeqHoKFN35Rl4YHx3DZNN9pm0o', // tslint:disable-next-line: max-line-length - KEYCLOAK_PUBLIC_KEY: 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoBa+pmsxpPhGED2/zexM/PL+rLv2YnLCbX2agY2fllucCvnPNmWRTyg8L+qgkJBpHuhcHqXEFTfBm2WCe4yke+0LnGi6SGsCJTrc2Sh+Lgo/L9r1lr7uOoD8BHE6ywnbHG2ok7qyomCTD9LpG1WXFk4T3WUH9+gN2vm77s3YJz2yfo2UlMd1Dp9nWZhfwB1vwJLuRHnqEKkSH8M2K8SFRdpgOyLdM9fprr7Ebgr7SWdoAy2q3mUONSmDsftKDeEWjr6MXGTATfbMfKE3GFqddMjFHkmvh42aM+RlU3N2xo2msF8M2sYF6YlIDkED+9h+y0QROJjQlBr774GAd06I0wIDAQAB', - KARMAYOGI_PORTAL_HOST: env.KARMAYOGI_PORTAL_HOST || 'igot-dev.in', + KEYCLOAK_PUBLIC_KEY: 'publicKey', + KARMAYOGI_PORTAL_HOST: env.KARMAYOGI_PORTAL_HOST || 'portal.karmayogi.nic.in', GOOGLE_CLIENT_ID: env.GOOGLE_CLIENT_ID || 'googleClientId', GOOGLE_CLIENT_SECRET: env.GOOGLE_CLIENT_SECRET || 'googleClientSecret', PARICHAY_CLIENT_ID: env.PARICHAY_CLIENT_ID || 'parichayClientId', PARICHAY_CLIENT_SECRET: env.PARICHAY_CLIENT_SECRET || 'parichayClientSecret', KEYCLOAK_GOOGLE_CLIENT_ID: env.KEYCLOAK_GOOGLE_CLIENT_ID || 'keycloakGoogleClientId', KEYCLOAK_GOOGLE_CLIENT_SECRET: env.KEYCLOAK_GOOGLE_CLIENT_SECRET || 'keycloakGoogleClientSecret', - PORTAL_AUTH_SERVER_URL: env.PORTAL_AUTH_SERVER_URL || 'https://igot-dev.in/auth', + PORTAL_AUTH_SERVER_URL: env.PORTAL_AUTH_SERVER_URL || 'https://portal.karmayogi.nic.in/auth', PARICHAY_CODE_CHALLENGE: env.PARICHAY_CODE_CHALLENGE || 'parichayCodeChallenge', PARICHAY_CODE_VERIFIER: env.PARICHAY_CODE_VERIFIER || 'parichayCodeVerfifier', PARICHAY_AUTH_URL: env.PARICHAY_AUTH_URL || 'parichayAuthUrl', @@ -178,7 +178,7 @@ export const CONSTANTS = { CUSTODIAN_ORG_ID: env.CUSTODIAN_ORG_ID || 'id', CUSTODIAN_ORG_CHANNEL: env.CUSTODIAN_ORG_CHANNEL || 'igot', PARICHAY_REVOKE_URL: env.PARICHAY_REVOKE_URL || 'parichayRevokeUrl', - + PORTAL_CREATE_NODEBB_USER: env.PORTAL_CREATE_NODEBB_USER || 'false', } export const RESTRICTED_PYTHON_STMT: string[] = process.env.RESTRICTED_CHARACTERS From d7cca403bddbae3429e17d98ac316cd7d09f4441 Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Tue, 5 Sep 2023 11:31:57 +0530 Subject: [PATCH 395/856] Added error handling while creating parichay user --- src/publicApi_v8/ssoUserHelper.ts | 37 ++++++++++++++++++------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/publicApi_v8/ssoUserHelper.ts b/src/publicApi_v8/ssoUserHelper.ts index 2e74c9fc..14307456 100644 --- a/src/publicApi_v8/ssoUserHelper.ts +++ b/src/publicApi_v8/ssoUserHelper.ts @@ -89,22 +89,29 @@ export async function createUserWithMailId(emailId: string, firstNameStr: string } else { _reqPayload = lodash.omit(_reqPayload, 'phone') } - const signUpResponse = await axios({ - ...axiosRequestConfig, - data: _reqPayload, - headers: { - Authorization: CONSTANTS.SB_API_KEY, - }, - method: 'POST', - url: API_END_POINTS.cbExtSignUpUser, - }) - statusString = signUpResponse.data.params.status - if (statusString.toUpperCase() !== 'SUCCESS') { - result.errMessage = signUpErr + 'FAILED_TO_CREATE_USER' - return Promise.resolve(result) + let signUpResponse + try { + signUpResponse = await axios({ + ...axiosRequestConfig, + data: _reqPayload, + headers: { + Authorization: CONSTANTS.SB_API_KEY, + }, + method: 'POST', + url: API_END_POINTS.cbExtSignUpUser, + }) + statusString = signUpResponse.data.params.status + if (statusString.toUpperCase() !== 'SUCCESS') { + result.errMessage = signUpErr + 'FAILED_TO_CREATE_USER' + } else { + result.userCreated = true + result.userId = signUpResponse.data.result.userId + } + } catch (signUpErr) { + const errMsg = signUpErr.response.data.params.errmsg + logError ('Failed to create User, error msg : ' + errMsg) + result.errMessage = errMsg } - result.userCreated = true - result.userId = signUpResponse.data.result.userId return Promise.resolve(result) } From 85294176ebfd6bbaddd88cb40a36cc9eb1c5603a Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Tue, 5 Sep 2023 18:05:35 +0530 Subject: [PATCH 396/856] Added necessary headers for getUserProgress API --- src/authoring/content/index.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/authoring/content/index.ts b/src/authoring/content/index.ts index e6390f5b..6dbe2c78 100644 --- a/src/authoring/content/index.ts +++ b/src/authoring/content/index.ts @@ -306,10 +306,20 @@ authApi.post('/batch/:key', async (req: Request, res: Response) => { res.status(400).send('Unsupported operation name - ' + key) break } + let rootOrgId = '' + if (req && req.session && req.session.hasOwnProperty('rootOrgId')) { + rootOrgId = req.session.rootOrgId + } + let channel = '' + if (req && req.session && req.session.hasOwnProperty('channel')) { + channel = req.session.channel + } const response = await axios.post(targetUrl, req.body, { ...axiosRequestConfig, headers: { Authorization: CONSTANTS.SB_API_KEY, + 'x-authenticated-user-channel': channel, + 'x-authenticated-user-orgid': rootOrgId, // tslint:disable-next-line: no-duplicate-string 'x-authenticated-user-token': extractUserToken(req), }, From fa1d5848b43ed750cb6c81068ac8e06fc656aeed Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Wed, 20 Sep 2023 14:44:36 +0530 Subject: [PATCH 397/856] Merged PR #182, #183 and #184 manually --- src/proxies_v8/proxies_v8.ts | 21 +++++++ src/utils/whitelistApis.ts | 109 +++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) diff --git a/src/proxies_v8/proxies_v8.ts b/src/proxies_v8/proxies_v8.ts index ede12765..7aa040bf 100644 --- a/src/proxies_v8/proxies_v8.ts +++ b/src/proxies_v8/proxies_v8.ts @@ -519,6 +519,27 @@ proxiesV8.use('/workflow/*', proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) ) +proxiesV8.use('/blendedprogram/*', + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + +proxiesV8.use('/batchsesion/*', + // tslint:disable-next-line: max-line-length + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + +proxiesV8.use('/course/*', + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + +proxiesV8.use('/faq/*', + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + +proxiesV8.use('/curatedprogram/*', + proxyCreatorSunbird(express.Router(), `${CONSTANTS.KONG_API_BASE}`) +) + function removePrefix(prefix: string, s: string) { return s.substr(prefix.length) } diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index b0e8b340..e6706ba3 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -278,6 +278,7 @@ export const API_LIST = { ROLE.CONTENT_CREATOR, ROLE.MDO_ADMIN, ROLE.MDO_LEADER, + ROLE.PROGRAM_COORDINATOR, ], }, '/proxies/v8/dashboard/analytics/getDashboardConfig/Karmayogi/:comp': { @@ -386,6 +387,7 @@ export const API_LIST = { ROLE.EDITOR, ROLE.MDO_ADMIN, ROLE.MDO_LEADER, + ROLE.PROGRAM_COORDINATOR, ], }, '/proxies/v8/v1/content/retire': { @@ -1607,6 +1609,7 @@ export const API_LIST = { ROLE.CONTENT_CREATOR, ROLE.MDO_ADMIN, ROLE.MDO_LEADER, + ROLE.PROGRAM_COORDINATOR, ], }, '/authApi/readCert/:certId': { @@ -2207,6 +2210,7 @@ export const API_LIST = { ROLE.PROGRAM_COORDINATOR, ROLE.MDO_ADMIN, ROLE.MDO_LEADER, + ROLE.PROGRAM_COORDINATOR, ], }, '/proxies/v8/workflow/blendedprogram/user/search': { @@ -2256,6 +2260,98 @@ export const API_LIST = { ROLE.CBP_ADMIN, ], }, + '/proxies/v8/batchsesion/qrcode/:courseid/:batchid': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/workflow/blendedprogram/admin/enrol' : { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ], + }, + '/proxies/v8/blendedprogram/v1/update/progress': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PROGRAM_COORDINATOR, + ], + }, + '/proxies/v8/workflow/blendedprogram/remove': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, + ], + }, + '/proxies/v8/course/v1/batch/read/:id': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/blendedprogram/v1/getUserContentProgress': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PROGRAM_COORDINATOR, + ], + }, + '/proxies/v8/user/v1/feed/:userId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/user/feed/v1/create': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/user/feed/v1/delete': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/user/feed/v1/update': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/faq/v1/assistant/configs/language': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/faq/v1/assistant/available/language': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + '/proxies/v8/curatedprogram/v1/enrol': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: [ @@ -2546,5 +2642,18 @@ export const API_LIST = { '/proxies/v8/workflow/blendedprogram/read/pc/:id', '/proxies/v8/workflow/blendedprogram/v1/stats', '/proxies/v8/storage/v1/report/:reportType/:date/:orgId/:fileName', + '/proxies/v8/workflow/blendedprogram/admin/enrol', + '/proxies/v8/batchsesion/qrcode/:courseid/:batchid', + '/proxies/v8/blendedprogram/v1/update/progress', + '/proxies/v8/workflow/blendedprogram/remove', + '/proxies/v8/course/v1/batch/read/:id', + '/proxies/v8/blendedprogram/v1/getUserContentProgress', + '/proxies/v8/user/v1/feed/:userId', + '/proxies/v8/user/feed/v1/create', + '/proxies/v8/user/feed/v1/delete', + '/proxies/v8/user/feed/v1/update', + '/proxies/v8/faq/v1/assistant/configs/language', + '/proxies/v8/faq/v1/assistant/available/language', + '/proxies/v8/curatedprogram/v1/enrol', ], } From 908d9bc5c0afc5d93f1733b5a6d1404ab007ecba Mon Sep 17 00:00:00 2001 From: Sahil-tarento <140611066+Sahil-tarento@users.noreply.github.com> Date: Mon, 25 Sep 2023 18:43:30 +0530 Subject: [PATCH 398/856] Added the New Role for SPV_PUBLISHER (#185) --- src/utils/roles.ts | 1 + src/utils/whitelistApis.ts | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/utils/roles.ts b/src/utils/roles.ts index 59e0336f..3cd912af 100644 --- a/src/utils/roles.ts +++ b/src/utils/roles.ts @@ -18,6 +18,7 @@ export const ROLE = { PROGRAM_COORDINATOR: 'PROGRAM_COORDINATOR', PUBLIC: 'PUBLIC', SPV_ADMIN: 'SPV_ADMIN', + SPV_PUBLISHER: 'SPV_PUBLISHER', STATE_ADMIN: 'STATE_ADMIN', WAT_MEMBER: 'WAT_MEMBER', } diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index e6706ba3..f7aaf7b7 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -209,6 +209,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CONTENT_REVIEWER, ROLE.CONTENT_PUBLISHER, + ROLE.SPV_PUBLISHER, ], }, '/proxies/v8/discussion/forum/v2/read': { @@ -259,6 +260,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CONTENT_PUBLISHER, ROLE.CONTENT_REVIEWER, + ROLE.SPV_PUBLISHER, ], }, '/proxies/v8/action/content/v3/hierarchy/update': { @@ -269,6 +271,7 @@ export const API_LIST = { ROLE.EDITOR, ROLE.CONTENT_PUBLISHER, ROLE.CONTENT_REVIEWER, + ROLE.SPV_PUBLISHER, ], }, '/proxies/v8/action/content/v3/create': { @@ -411,6 +414,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CONTENT_REVIEWER, ROLE.CONTENT_PUBLISHER, + ROLE.SPV_PUBLISHER, ], }, '/proxies/v8/action/content/v3/review/:do_id': { @@ -420,6 +424,7 @@ export const API_LIST = { ROLE.CONTENT_CREATOR, ROLE.CONTENT_REVIEWER, ROLE.CONTENT_PUBLISHER, + ROLE.SPV_PUBLISHER, ], }, '/proxies/v8/action/content/v3/publish/:do_id': { @@ -428,6 +433,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CONTENT_PUBLISHER, ROLE.MDO_ADMIN, + ROLE.SPV_PUBLISHER, ], }, '/proxies/v8/action/content/v3/update/:do_id': { @@ -440,6 +446,7 @@ export const API_LIST = { ROLE.CONTENT_PUBLISHER, ROLE.MDO_ADMIN, ROLE.MDO_LEADER, + ROLE.SPV_PUBLISHER, ], }, '/proxies/v8/data/v1/system/settings/get/orgTypeList': { @@ -589,6 +596,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.CONTENT_PUBLISHER, + ROLE.SPV_PUBLISHER, ], }, '/proxies/v8/learnervm/private/content/v3/review/:do_id': { @@ -1183,6 +1191,7 @@ export const API_LIST = { ROLE.EDITOR, ROLE.CONTENT_PUBLISHER, ROLE.CONTENT_REVIEWER, + ROLE.SPV_PUBLISHER, ], }, // '/protected/v8/connections/connections/recommended': { @@ -1256,6 +1265,7 @@ export const API_LIST = { ROLE.EDITOR, ROLE.CONTENT_PUBLISHER, ROLE.CONTENT_REVIEWER, + ROLE.SPV_PUBLISHER, ], }, '/protected/v8/profanity/getPdfProfanityForContent/:contentId': { @@ -1266,6 +1276,7 @@ export const API_LIST = { ROLE.CONTENT_REVIEWER, ROLE.CONTENT_PUBLISHER, ROLE.EDITOR, + ROLE.SPV_PUBLISHER, ], }, '/protected/v8/catalog': { @@ -1285,6 +1296,7 @@ export const API_LIST = { ROLE.CONTENT_REVIEWER, ROLE.EDITOR, ROLE.CONTENT_PUBLISHER, + ROLE.SPV_PUBLISHER, ], }, '/protected/v8/portal/mdo/deptAction/userrole': { @@ -1844,6 +1856,7 @@ export const API_LIST = { ROLE.CONTENT_REVIEWER, ROLE.MDO_ADMIN, ROLE.MDO_LEADER, + ROLE.SPV_PUBLISHER, ], }, '/proxies/v8/questionset/v1/create': { @@ -1861,6 +1874,7 @@ export const API_LIST = { ROLE.CONTENT_REVIEWER, ROLE.MDO_ADMIN, ROLE.MDO_LEADER, + ROLE.SPV_PUBLISHER, ], checksNeeded: [CHECK.ROLE], }, @@ -1871,6 +1885,7 @@ export const API_LIST = { ROLE.CONTENT_REVIEWER, ROLE.MDO_ADMIN, ROLE.MDO_LEADER, + ROLE.SPV_PUBLISHER, ], checksNeeded: [CHECK.ROLE], }, @@ -1887,6 +1902,7 @@ export const API_LIST = { ROLE.CONTENT_PUBLISHER, ROLE.MDO_ADMIN, ROLE.MDO_LEADER, + ROLE.SPV_PUBLISHER, ], checksNeeded: [CHECK.ROLE], }, @@ -1903,6 +1919,7 @@ export const API_LIST = { ROLE.CONTENT_CREATOR, ROLE.CONTENT_PUBLISHER, ROLE.CONTENT_REVIEWER, + ROLE.SPV_PUBLISHER, ], checksNeeded: [CHECK.ROLE], }, @@ -1910,6 +1927,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.CONTENT_PUBLISHER, ROLE.CONTENT_REVIEWER, + ROLE.SPV_PUBLISHER, ], checksNeeded: [CHECK.ROLE], }, @@ -1920,6 +1938,7 @@ export const API_LIST = { ROLE.CONTENT_REVIEWER, ROLE.MDO_ADMIN, ROLE.MDO_LEADER, + ROLE.SPV_PUBLISHER, ], checksNeeded: [CHECK.ROLE], }, From 42a42cd2490455e71a2aac7c783cc602a2792710 Mon Sep 17 00:00:00 2001 From: saipradeep_ravipati Date: Tue, 26 Sep 2023 16:36:40 +0530 Subject: [PATCH 399/856] New Role for Remove API & Enrol Status count API entry --- src/utils/whitelistApis.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index f7aaf7b7..ff364914 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2306,6 +2306,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.MDO_ADMIN, ROLE.MDO_LEADER, + ROLE.PROGRAM_COORDINATOR, ], }, '/proxies/v8/course/v1/batch/read/:id': { @@ -2371,6 +2372,14 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/blendedprogram/enrol/status/count': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, + }, URL_PATTERN: [ @@ -2674,5 +2683,6 @@ export const API_LIST = { '/proxies/v8/faq/v1/assistant/configs/language', '/proxies/v8/faq/v1/assistant/available/language', '/proxies/v8/curatedprogram/v1/enrol', + '/proxies/v8/blendedprogram/enrol/status/count', ], } From 96a07f5b5cc8c722fcf987722003f3157e50bb68 Mon Sep 17 00:00:00 2001 From: saipradeep_ravipati Date: Tue, 26 Sep 2023 17:24:37 +0530 Subject: [PATCH 400/856] Corrected the path --- src/utils/whitelistApis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index ff364914..422f91a5 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2683,6 +2683,6 @@ export const API_LIST = { '/proxies/v8/faq/v1/assistant/configs/language', '/proxies/v8/faq/v1/assistant/available/language', '/proxies/v8/curatedprogram/v1/enrol', - '/proxies/v8/blendedprogram/enrol/status/count', + '/proxies/v8/workflow/blendedprogram/enrol/status/count', ], } From f9de5e769f47b892277016c54a3c2b6e40341b9b Mon Sep 17 00:00:00 2001 From: saipradeep_ravipati Date: Tue, 26 Sep 2023 17:36:59 +0530 Subject: [PATCH 401/856] Corrected URL --- src/utils/whitelistApis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 422f91a5..acf8a468 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2372,7 +2372,7 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/proxies/v8/blendedprogram/enrol/status/count': { + '/proxies/v8/workflow/blendedprogram/enrol/status/count': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ From 108f57bebaf58c032a9ab6796ddf851437a8734c Mon Sep 17 00:00:00 2001 From: saipradeep_ravipati Date: Thu, 28 Sep 2023 06:43:39 +0530 Subject: [PATCH 402/856] Seperated Update Operation btw MDO and PC --- src/utils/whitelistApis.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index acf8a468..c4193822 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2239,15 +2239,21 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - '/proxies/v8/workflow/blendedprogram/update': { + '/proxies/v8/workflow/blendedprogram/update/pc': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.MDO_LEADER, ROLE.PROGRAM_COORDINATOR, ], }, + '/proxies/v8/workflow/blendedprogram/update/mdo': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, + ], + }, '/proxies/v8/workflow/blendedprogram/read/mdo/:id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -2665,7 +2671,8 @@ export const API_LIST = { '/proxies/v8/workflow/blendedprogram/enrol', '/proxies/v8/workflow/blendedprogram/search', '/proxies/v8/workflow/blendedprogram/user/search', - '/proxies/v8/workflow/blendedprogram/update', + '/proxies/v8/workflow/blendedprogram/update/pc', + '/proxies/v8/workflow/blendedprogram/update/mdo', '/proxies/v8/workflow/blendedprogram/read/mdo/:id', '/proxies/v8/workflow/blendedprogram/read/pc/:id', '/proxies/v8/workflow/blendedprogram/v1/stats', From cedeed239ed778c90addb3e7b115e1ba9027fe4a Mon Sep 17 00:00:00 2001 From: saipradeep_ravipati Date: Thu, 28 Sep 2023 11:46:42 +0530 Subject: [PATCH 403/856] SearchV2 Apis white-listed --- src/utils/whitelistApis.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index c4193822..53f631f4 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2232,6 +2232,21 @@ export const API_LIST = { ROLE.PROGRAM_COORDINATOR, ], }, + '/proxies/v8/workflow/blendedprogram/searchv2/pc': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PROGRAM_COORDINATOR, + ], + }, + '/proxies/v8/workflow/blendedprogram/searchv2/mdo': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, + ], + }, '/proxies/v8/workflow/blendedprogram/user/search': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -2670,6 +2685,8 @@ export const API_LIST = { '/proxies/v8/ratings/v2/read', '/proxies/v8/workflow/blendedprogram/enrol', '/proxies/v8/workflow/blendedprogram/search', + '/proxies/v8/workflow/blendedprogram/searchv2/pc', + '/proxies/v8/workflow/blendedprogram/searchv2/mdo', '/proxies/v8/workflow/blendedprogram/user/search', '/proxies/v8/workflow/blendedprogram/update/pc', '/proxies/v8/workflow/blendedprogram/update/mdo', From 0ef66f5b5e40d1cf8c962c4ef71bc52e298d74c1 Mon Sep 17 00:00:00 2001 From: dkttarento <138442957+dkttarento@users.noreply.github.com> Date: Thu, 28 Sep 2023 19:08:44 +0530 Subject: [PATCH 404/856] Update whitelistApis.ts Added Whitelist UI-Proxy for Blended Program/Unenrol -> URL_PATTERN , & API_LIST. --- src/utils/whitelistApis.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 53f631f4..f2df4f4e 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2400,6 +2400,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/proxies/v8/workflow/blendedprogram/unenrol': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, }, URL_PATTERN: @@ -2708,5 +2715,6 @@ export const API_LIST = { '/proxies/v8/faq/v1/assistant/available/language', '/proxies/v8/curatedprogram/v1/enrol', '/proxies/v8/workflow/blendedprogram/enrol/status/count', + '/proxies/v8/workflow/blendedprogram/unenrol' ], } From 3e84b9d4984402f90ff8876b2cd469c0bbbdad2d Mon Sep 17 00:00:00 2001 From: SaipradeepR <53404427+SaipradeepR@users.noreply.github.com> Date: Thu, 28 Sep 2023 19:13:05 +0530 Subject: [PATCH 405/856] Update whitelistApis.ts --- src/utils/whitelistApis.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index f2df4f4e..aed62a52 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2715,6 +2715,6 @@ export const API_LIST = { '/proxies/v8/faq/v1/assistant/available/language', '/proxies/v8/curatedprogram/v1/enrol', '/proxies/v8/workflow/blendedprogram/enrol/status/count', - '/proxies/v8/workflow/blendedprogram/unenrol' + '/proxies/v8/workflow/blendedprogram/unenrol', ], } From 8ad4113a89cde99cb325dd3ba3ec93098270c979 Mon Sep 17 00:00:00 2001 From: saipradeep_ravipati Date: Fri, 29 Sep 2023 12:33:32 +0530 Subject: [PATCH 406/856] Added WhiteList for PC and MDO Remove urls --- src/utils/whitelistApis.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index aed62a52..67c37186 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2321,15 +2321,21 @@ export const API_LIST = { ROLE.PROGRAM_COORDINATOR, ], }, - '/proxies/v8/workflow/blendedprogram/remove': { + '/proxies/v8/workflow/blendedprogram/remove/pc': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.MDO_ADMIN, - ROLE.MDO_LEADER, ROLE.PROGRAM_COORDINATOR, ], }, + '/proxies/v8/workflow/blendedprogram/remove/mdo': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, + ], + }, '/proxies/v8/course/v1/batch/read/:id': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -2704,7 +2710,8 @@ export const API_LIST = { '/proxies/v8/workflow/blendedprogram/admin/enrol', '/proxies/v8/batchsesion/qrcode/:courseid/:batchid', '/proxies/v8/blendedprogram/v1/update/progress', - '/proxies/v8/workflow/blendedprogram/remove', + '/proxies/v8/workflow/blendedprogram/remove/pc', + '/proxies/v8/workflow/blendedprogram/remove/mdo', '/proxies/v8/course/v1/batch/read/:id', '/proxies/v8/blendedprogram/v1/getUserContentProgress', '/proxies/v8/user/v1/feed/:userId', From fc07a1adffdd089af23081e6680ff4a7694592f2 Mon Sep 17 00:00:00 2001 From: dkttarento <138442957+dkttarento@users.noreply.github.com> Date: Fri, 29 Sep 2023 17:23:54 +0530 Subject: [PATCH 407/856] Update whitelistApis.ts (#193) Added Whitelist API for -> storage/v1/reportInfo/:orgId --- src/utils/whitelistApis.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 67c37186..94a03063 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2413,7 +2413,14 @@ export const API_LIST = { ROLE.PUBLIC, ], }, - + '/proxies/v8/storage/v1/reportInfo/:orgId': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, + ], + }, }, URL_PATTERN: [ @@ -2723,5 +2730,6 @@ export const API_LIST = { '/proxies/v8/curatedprogram/v1/enrol', '/proxies/v8/workflow/blendedprogram/enrol/status/count', '/proxies/v8/workflow/blendedprogram/unenrol', + '/proxies/v8/storage/v1/reportInfo/:orgId', ], } From dbd58a77046688d0cfca65e36e53f0f77b28b25b Mon Sep 17 00:00:00 2001 From: karthik-tarento Date: Sun, 1 Oct 2023 13:08:34 +0530 Subject: [PATCH 408/856] Fixed build issue --- src/utils/whitelistApis.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 94a03063..b7a3e9e8 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2236,13 +2236,13 @@ export const API_LIST = { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ - ROLE.PROGRAM_COORDINATOR, + ROLE.PROGRAM_COORDINATOR, ], }, '/proxies/v8/workflow/blendedprogram/searchv2/mdo': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys - ROLE_CHECK: [ + ROLE_CHECK: [ ROLE.MDO_ADMIN, ROLE.MDO_LEADER, ], @@ -2266,7 +2266,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, - ROLE.MDO_LEADER, + ROLE.MDO_LEADER, ], }, '/proxies/v8/workflow/blendedprogram/read/mdo/:id': { @@ -2333,7 +2333,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, - ROLE.MDO_LEADER, + ROLE.MDO_LEADER, ], }, '/proxies/v8/course/v1/batch/read/:id': { @@ -2405,7 +2405,7 @@ export const API_LIST = { ROLE_CHECK: [ ROLE.PUBLIC, ], - }, + }, '/proxies/v8/workflow/blendedprogram/unenrol': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -2418,7 +2418,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, - ROLE.MDO_LEADER, + ROLE.MDO_LEADER, ], }, }, From b909c4bbd27df788a24ab1266a276b5eb9fd7271 Mon Sep 17 00:00:00 2001 From: Sahil-tarento <140611066+Sahil-tarento@users.noreply.github.com> Date: Tue, 3 Oct 2023 11:43:10 +0530 Subject: [PATCH 409/856] Added the MDO_LEADER changes (#197) --- src/utils/whitelistApis.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index b7a3e9e8..333b7e84 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -2297,6 +2297,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ROLE.CBP_ADMIN, ], }, @@ -2312,6 +2313,7 @@ export const API_LIST = { // tslint:disable-next-line: object-literal-sort-keys ROLE_CHECK: [ ROLE.MDO_ADMIN, + ROLE.MDO_LEADER, ], }, '/proxies/v8/blendedprogram/v1/update/progress': { From de1dddea36ec2939590d8d22ade646d35e545799 Mon Sep 17 00:00:00 2001 From: Sahil-tarento <140611066+Sahil-tarento@users.noreply.github.com> Date: Wed, 4 Oct 2023 18:42:32 +0530 Subject: [PATCH 410/856] Email Template Changes for Welcome Email (#198) * Changes for Mail template (#188) * Changes for Mail template * Added the New Role for SPV_PUBLISHER (#185) * Build issues resolved * Using proper link for welcome email --------- Co-authored-by: karthik-tarento --- src/protectedApi_v8/user/profile-details.ts | 5 ++++- src/utils/env.ts | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/protectedApi_v8/user/profile-details.ts b/src/protectedApi_v8/user/profile-details.ts index 31ef1580..73ff85b0 100644 --- a/src/protectedApi_v8/user/profile-details.ts +++ b/src/protectedApi_v8/user/profile-details.ts @@ -368,13 +368,16 @@ profileDeatailsApi.post('/createUser', async (req, res) => { const welcomeMailRequest = { allowedLoging: 'You can use your email to Login', body: 'Hello', - emailTemplateType: 'iGotWelcome_v3', + discoverLink: CONSTANTS.HTTPS_HOST + '/page/learn', + emailTemplateType: 'iGotWelcome_v4', firstName: sbUserProfile.firstName, link: passwordResetResponse.data.result.link, + meetingLink: CONSTANTS.NINE_TO_FIVE_MEETING_LINK, mode: 'email', orgName: sbChannel, recipientEmails: [ sbemail_ ], setPasswordLink: true, + signinLink: CONSTANTS.HTTPS_HOST + '/protected/v8/resource', subject: 'Welcome to iGOT Karmayogi... Activate your account now!', welcomeMessage: 'Hello', } diff --git a/src/utils/env.ts b/src/utils/env.ts index d74d8218..bd112428 100644 --- a/src/utils/env.ts +++ b/src/utils/env.ts @@ -179,6 +179,7 @@ export const CONSTANTS = { CUSTODIAN_ORG_CHANNEL: env.CUSTODIAN_ORG_CHANNEL || 'igot', PARICHAY_REVOKE_URL: env.PARICHAY_REVOKE_URL || 'parichayRevokeUrl', PORTAL_CREATE_NODEBB_USER: env.PORTAL_CREATE_NODEBB_USER || 'false', + NINE_TO_FIVE_MEETING_LINK: env.NINE_TO_FIVE_MEETING_LINK || '', } export const RESTRICTED_PYTHON_STMT: string[] = process.env.RESTRICTED_CHARACTERS From d90e4682e709a73098bb7ee347fe1c43effa787d Mon Sep 17 00:00:00 2001 From: saipradeep_ravipati Date: Thu, 5 Oct 2023 14:29:50 +0530 Subject: [PATCH 411/856] Added Dept Filter for getting getUserOfBatch --- src/protectedApi_v8/cohorts.ts | 9 ++++++--- src/utils/whitelistApis.ts | 8 ++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/protectedApi_v8/cohorts.ts b/src/protectedApi_v8/cohorts.ts index fe13f8ed..cffa5d3f 100644 --- a/src/protectedApi_v8/cohorts.ts +++ b/src/protectedApi_v8/cohorts.ts @@ -254,9 +254,10 @@ cohortsApi.get('/course/batch/cert/download/:certId', async (req, res) => { } }) -cohortsApi.get('/course/getUsersForBatch/:batchId', async (req, res) => { +cohortsApi.get('/course/getUsersForBatch/:batchId/:deptName?', async (req, res) => { try { const batchId = req.params.batchId + const deptName = req.params.deptName const reqBody = { request: { batch: { @@ -289,8 +290,10 @@ cohortsApi.get('/course/getUsersForBatch/:batchId', async (req, res) => { if (searchresponse.data.result.response.count > 0) { for (const profileObj of searchresponse.data.result.response.content) { const user: ICohortsUser = getUsers(profileObj) - user.department = profileObj.channel - userlist.push(user) + if (!deptName || (profileObj.channel && profileObj.channel === deptName)) { + user.department = profileObj.channel; + userlist.push(user); + } } } } diff --git a/src/utils/whitelistApis.ts b/src/utils/whitelistApis.ts index 333b7e84..6afe157e 100644 --- a/src/utils/whitelistApis.ts +++ b/src/utils/whitelistApis.ts @@ -1250,6 +1250,13 @@ export const API_LIST = { ROLE.PUBLIC, ], }, + '/protected/v8/cohorts/course/getUsersForBatch/:batchId/:deptName': { + checksNeeded: [CHECK.ROLE], + // tslint:disable-next-line: object-literal-sort-keys + ROLE_CHECK: [ + ROLE.PUBLIC, + ], + }, '/protected/v8/cohorts/user/autoenrollment/:courseId': { checksNeeded: [CHECK.ROLE], // tslint:disable-next-line: object-literal-sort-keys @@ -2589,6 +2596,7 @@ export const API_LIST = { // '/protected/v8/connections/add/connection', // '/protected/v8/connections/connections/suggests', '/protected/v8/cohorts/course/getUsersForBatch/:courseId', + '/protected/v8/cohorts/course/getUsersForBatch/:batchId/:deptName', '/protected/v8/cohorts/user/autoenrollment/:courseId', '/protected/v8/profanity/startPdfProfanity', '/protected/v8/profanity/getPdfProfanityForContent/:contentId', From 0efea7b03784141ac82e03838737fe87179e3bc8 Mon Sep 17 00:00:00 2001 From: Rajesh Kumaravel Date: Fri, 6 Oct 2023 16:04:50 +0200 Subject: [PATCH 412/856] Issue fix: Mobile SVG decoder added --- src/protectedApi_v8/cohorts.ts | 17 +++++++++++++++++ src/utils/whitelistApis.ts | 7 +++++++ 2 files changed, 24 insertions(+) diff --git a/src/protectedApi_v8/cohorts.ts b/src/protectedApi_v8/cohorts.ts index cffa5d3f..c9059c1c 100644 --- a/src/protectedApi_v8/cohorts.ts +++ b/src/protectedApi_v8/cohorts.ts @@ -254,6 +254,23 @@ cohortsApi.get('/course/batch/cert/download/:certId', async (req, res) => { } }) +cohortsApi.post('/course/batch/cert/download/mobile', async (req, res) => { + try { + const svgContent = req.body.printUri + let _decodedSvg = decodeURIComponent(svgContent.replace(/data:image\/svg\+xml,/, '')).replace(/\