Skip to content

Commit

Permalink
Merge pull request #155 from Sanketika-Obsrv/open-source
Browse files Browse the repository at this point in the history
feat: #OBS-I466 open source version of v2 web console
  • Loading branch information
HarishGangula authored Jan 6, 2025
2 parents 4e1fd22 + 7f95260 commit 8ee2590
Show file tree
Hide file tree
Showing 135 changed files with 5,067 additions and 4,050 deletions.
14 changes: 8 additions & 6 deletions src/main/controllers/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export default {
"AUTHENTICATION_ALLOWED_TYPES": appConfig.AUTHENTICATION_ALLOWED_TYPES,
"ENV": appConfig.ENV,
"BASE_URL": appConfig.BASE_URL,
"OBSRV_NLQ_CONFIG": appConfig.OBSRV_NLQ_CONFIG,
"STORAGE_TYPES": appConfig.STORAGE_TYPES,
"validationLimit": {
"datasetMaxLen": 100,
"datasetMinLen": 4,
Expand All @@ -20,12 +22,12 @@ export default {
"brokerServerLen": 500,
"fieldDescriptionMaxLen": 200,
"maxTag": 5,
"alertRuleNameMaxLen":100,
"alertRuleLabelsMaxLen":100,
"notificationChannelNameMaxLen":100,
"transformationFieldMaxLen":100,
"denormInputFieldMaxLen":100,
"alertsPerPage":10
"alertRuleNameMaxLen": 100,
"alertRuleLabelsMaxLen": 100,
"notificationChannelNameMaxLen": 100,
"transformationFieldMaxLen": 100,
"denormInputFieldMaxLen": 100,
"alertsPerPage": 10
},
"AUTHENTICATION_TYPE": appConfig.AUTHENTICATION_TYPE
})
Expand Down
8 changes: 8 additions & 0 deletions src/main/controllers/user_create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ export default {
handler: () => async (req: Request, res: Response, next: NextFunction) => {
try {
const userRequest = _.get(req, ['body', 'request']);
const isOwner = _.get(req, ['session', 'userDetails', 'is_owner']);

if (!isOwner && userRequest?.roles?.includes('admin')) {
return res.status(403).json({
error: 'Only an owner can assign the admin role'
});
}

userRequest.user_name = userRequest.user_name.trim().replace(/\s+/g, '_');
if (authenticationType === 'keycloak') {
const keycloakToken = JSON.parse(req?.session['keycloak-token']);
Expand Down
16 changes: 14 additions & 2 deletions src/main/controllers/user_manage_roles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@ import userService from '../services/oauthUsers';
import { transform } from '../../shared/utils/transformResponse';
import _ from 'lodash';

const mergeRoles = (currentRoles: any, newConfigs: any) => {
const mergeRoles = (currentRoles: any, newConfigs: any, isOwner: boolean) => {
const rolesToRemove = _.map(_.filter(newConfigs, { action: 'remove' }), 'value');
const rolesToAdd = _.map(_.filter(newConfigs, { action: 'upsert' }), 'value');
const conflictRoles = _.intersection(rolesToRemove, rolesToAdd);
if (conflictRoles.length > 0) {
throw new Error(`Can not upsert and remove the same role(s) at the same time: ${conflictRoles.join(', ')}`);
}

if (!isOwner) {
if (rolesToAdd.includes('admin') || rolesToRemove.includes('admin')) {
throw new Error('Only the owner can modify the admin role');
}
}

return _.union(_.pullAll(currentRoles, rolesToRemove), rolesToAdd);
};

Expand All @@ -19,8 +26,9 @@ export default {
try {
const { user_name, roles } = _.get(req, ['body', 'request']);

const isOwner = _.get(req, ['session', 'userDetails', 'is_owner']);
const user = await userService.find({ user_name });
const updatedRoles = mergeRoles(_.get(user, ['roles']), roles);
const updatedRoles = mergeRoles(_.get(user, ['roles']), roles, isOwner);
const result = await userService.update(
{ user_name },
{
Expand All @@ -35,6 +43,10 @@ export default {
const extendedError = Object.assign(err, { message: error, status: 404, responseCode: 'NOT_FOUND', errorCode: 'NOT_FOUND' });
next(extendedError);
} else {
const e = error as Error;
if (e.message && (e.message.includes('Only the owner can modify the admin role'))) {
return res.status(403).json({ error: e.message });
}
next(error);
}
}
Expand Down
11 changes: 11 additions & 0 deletions src/main/controllers/user_manage_status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,18 @@ export default {
handler: () => async (req: Request, res: Response, next: NextFunction) => {
try {
const { user_name, status } = _.get(req, ['body', 'request']);
const isOwner = _.get(req, ['session', 'userDetails', 'is_owner']);

const user = await userService.find({ user_name });

const hasAdminRole = user?.roles.includes('admin');

if (hasAdminRole && !isOwner) {
return res.status(403).json({
error: 'Only the owner can change the status of an admin user'
});
}

const result = await userService.update(
{ user_name },
{
Expand Down
13 changes: 1 addition & 12 deletions src/main/controllers/user_read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,7 @@ export default {
const { user_name } = _.get(request, ['params']);
const sessionUserDetails = getUserDetails(request);
const sessionUserName = sessionUserDetails?.sessionUserName;
if (user_name !== sessionUserName) {
response.status(403).json(
transform({
params: {
err: 'FORBIDDEN',
errmsg: 'Access denied',
},
responseCode: 'FORBIDDEN',
}),
);
}
const user = await userService.find({ user_name });
const user = await userService.find({ user_name: sessionUserName });
const { password, ...userInfo } = user;
const responseData = {
id: 'api.user.read',
Expand Down
2 changes: 2 additions & 0 deletions src/main/helpers/proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const onProxyRes = ({ entity }: any) => (proxyReq: any, req: any, res: Re
export const onProxyReq = ({ entity }: any) => (proxyReq: any, req: any, res: Response) => {
const startTime = Date.now();
req.startTime = startTime;

if (entity !== promEntities.alerts) {
if(authenticationType === 'keycloak'){
const keycloakToken = JSON.parse(req?.session['keycloak-token']);
Expand All @@ -34,5 +35,6 @@ export const onProxyReq = ({ entity }: any) => (proxyReq: any, req: any, res: Re
proxyReq.setHeader('Authorization', `Bearer ${jwtToken}`);
}
}

incrementApiCalls({ entity, endpoint: req.url });
}
11 changes: 8 additions & 3 deletions src/main/middlewares/passportAuthenticate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@ import _ from 'lodash';
import passport from 'passport';
import appConfig from '../../shared/resources/appConfig';
import { User } from '../types';
import { logger } from '../../shared/utils/logger';

const baseURL = appConfig.BASE_URL;
const private_key: string = appConfig.USER_TOKEN_PRIVATE_KEY;
const expiresIn = appConfig.USER_TOKEN_EXPIRY;

const generateToken = (user: User) => {
const payload = _.pick(user, ['id', 'user_name', 'email_address', 'roles']);
const payload = _.pick(user, ['id', 'user_name', 'email_address', 'roles', 'is_owner']);
return new Promise((resolve, reject) => {
jwt.sign(payload, private_key, { algorithm: 'RS256' }, (err, token) => {
jwt.sign(payload, private_key, { algorithm: 'RS256', expiresIn: expiresIn }, (err, token) => {
if (err) {
return reject(err);
}
Expand All @@ -24,23 +26,26 @@ export default {
handler: () => (req: Request, res: Response, next: NextFunction) => {
passport.authenticate('local', (err: Error, user: User) => {
if (err) {
logger.error("err:", err)
return next(err);
}
if (!user) {
return res.redirect(`${baseURL}/login`);
}
return req.login(user, (loginErr) => {
if (loginErr) {
logger.error("loginErr: ", loginErr)
return next(loginErr);
}
return generateToken(user)
.then((token: any) => {
req.session.token = token;
req.session.roles = _.get(user, ['roles']);
req.session.userDetails = _.pick(user, ['id', 'user_name', 'email_address', 'roles']);
req.session.userDetails = _.pick(user, ['id', 'user_name', 'email_address', 'roles', 'is_owner']);
return res.redirect(baseURL || '/');
})
.catch((tokenError) => {
logger.error("tokenError: ", tokenError)
return next(tokenError);
});
});
Expand Down
3 changes: 2 additions & 1 deletion src/main/resources/prometheusEntities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ export default {
"dataset": "obsrv-dataset",
"system": "command",
"alerts": "alert-manager",
"config": "obsrv-config"
"config": "obsrv-config",
"management": "obsrv-management"
}
2 changes: 1 addition & 1 deletion src/main/resources/routesConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ export default [
path: 'user',
routes: [
{
path: 'read/:user_name',
path: 'read',
method: 'GET',
middlewares: [setContext.handler(permissions.ReadUser), ensureLoggedInMiddleware, controllers.get('user:read')?.handler({})],
},
Expand Down
9 changes: 9 additions & 0 deletions src/shared/databases/postgres.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ const find = async (table: string, conditions: IPostgres, jsonbType: Array<strin

const values = Object.values(conditions);

if (columns.length === 0) {
const query = {
text: `SELECT * FROM ${table}`,
values: [],
};
const result = await pool.query<IPostgres>(query);
return result.rows;
}

const whereClause = columns
.map((column, i) => {
if (arrayType.includes(column)) {
Expand Down
4 changes: 2 additions & 2 deletions src/shared/middlewares/globalErrorHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { NextFunction, Request, Response } from 'express';
import { transform } from '../utils/transformResponse';
import appConfig from '../resources/appConfig';
import { logger } from '../utils/logger';

export default {
name: 'globalErrorHandler',
Expand All @@ -14,11 +15,10 @@ export default {
} = error;

const { id = 'api' } = request.responsePayload || {};

logger.error(error)
if (request.url.includes("oauth/v1/login")) {
return response.redirect(`${appConfig.BASE_URL}/login?err=Invalid Credentials`);
}

response.status(status).json(transform({ id, responseCode, params: { err: errorCode, errmsg: message } }));
},
};
6 changes: 6 additions & 0 deletions src/shared/resources/appConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ export default {
ENV: env.ENV || 'development',
AUTHENTICATION_ALLOWED_TYPES: env.AUTHENTICATION_ALLOWED_TYPES || 'obsrv,ad,google',
DEFAULT_ALERT_MANAGER: env.ALERT_MANAGER || 'grafana',
OBSRV_NLQ_CONFIG: {
URL: env.OBSRV_NLQ_URL || 'http://localhost:8501',
IS_ENABLED: env.IS_NLQ_ENABLED || 'false'
},
PROMETHEUS: {
URL: env.PROMETHEUS_URL || 'http://localhost:9090',
},
Expand Down Expand Up @@ -69,4 +73,6 @@ export default {
PUBLIC_CLIENT: env.KEYCLOAK_PUBLIC_CLIENT || 'false',
SSL_REQUIRED: env.KEYCLOAK_SSL_REQUIRED || 'external',
},
USER_TOKEN_EXPIRY: env.USER_TOKEN_EXPIRY || '1d',
STORAGE_TYPES: env.STORAGE_TYPES || '{"lake_house":true,"realtime_store":true}'
};
50 changes: 25 additions & 25 deletions web-console-v2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,60 +10,60 @@
"@emotion/react": "11.11.4",
"@emotion/styled": "11.11.5",
"@fontsource/montserrat": "^5.0.18",
"@monaco-editor/react": "4.6.0",
"@mui/icons-material": "^5.16.7",
"@mui/lab": "5.0.0-alpha.173",
"@mui/material": "^5.16.7",
"@mui/styles": "^6.1.5",
"@mui/system": "^5.10.9",
"@monaco-editor/react": "4.7.0-rc.0",
"@mui/icons-material": "^6.3.0",
"@mui/lab": "6.0.0-beta.21",
"@mui/material": "^6.3.0",
"@mui/styles": "^6.3.0",
"@mui/system": "^6.3.0",
"@mui/x-charts": "^7.15.0",
"@mui/x-date-pickers": "^7.23.3",
"@rjsf/core": "^5.18.3",
"@rjsf/mui": "^5.18.3",
"@rjsf/utils": "^5.18.4",
"@rjsf/validator-ajv8": "^5.18.3",
"@tanstack/react-query": "5.51.15",
"@tanstack/react-query": "5.62.11",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.97",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0",
"ajv-errors": "3.0.0",
"apexcharts": "^3.53.0",
"axios": "^1.7.7",
"dayjs": "^1.11.13",
"formik": "2.4.6",
"framer-motion": "^7.5.3",
"match-sorter": "^6.3.1",
"framer-motion": "^12.0.0-alpha.2",
"history": "^5.3.0",
"jsonata": "2.0.2",
"jsoneditor": "^9.10.4",
"jsoneditor-react": "^3.1.2",
"lodash": "4.17.21",
"lottie-web": "^5.12.2",
"match-sorter": "^6.3.1",
"material-react-table": "^3.0.1",
"millify": "^6.1.0",
"moment": "2.30.1",
"pretty-bytes": "^6.1.1",
"pretty-ms": "^9.1.0",
"react": "^18.3.1",
"react": "19.0.0",
"react-apexcharts": "^1.4.1",
"react-device-detect": "^2.2.2",
"@mui/x-date-pickers": "^5.0.4",
"jsoneditor": "^9.10.4",
"jsoneditor-react": "^3.1.2",
"react-dom": "^18.3.1",
"react-dom": "19.0.0",
"react-dropzone": "14.2.3",
"react-gauge-chart": "^0.5.1",
"react-intl": "6.6.8",
"react-intl": "7.0.2",
"react-lottie": "1.2.4",
"react-router-dom": "^6.23.0",
"react-scripts": "5.0.1",
"react-split-pane": "2.0.3",
"react-split-pane": "^0.1.92",
"react-table": "7.8.0",
"simplebar-react": "^2.4.1",
"typescript": "^4.9.5",
"uuid": "^10.0.0",
"web-vitals": "^2.1.4",
"yup": "^1.4.0",
"history": "^5.3.0"
"yup": "^1.4.0"
},
"scripts": {
"start": "react-scripts start",
Expand Down Expand Up @@ -93,20 +93,20 @@
]
},
"devDependencies": {
"@types/react-json-editor-ajrm": "^2.5.3",
"@eslint/js": "^9.2.0",
"@tanstack/eslint-plugin-query": "5.51.15",
"@tanstack/react-query-devtools": "5.51.15",
"@testing-library/jest-dom": "5.17.0",
"@testing-library/react": "13.4.0",
"@testing-library/react": "16.1.0",
"@testing-library/user-event": "13.5.0",
"@types/jest": "27.5.2",
"@types/lodash": "^4.17.7",
"@types/node": "16.18.97",
"@types/pg": "^8.11.6",
"@types/react": "18.2.0",
"@types/react-dom": "18.2.0",
"@types/react": "19.0.2",
"@types/react-dom": "19.0.2",
"@types/react-gauge-chart": "^0.4.3",
"@types/react-json-editor-ajrm": "^2.5.3",
"@types/react-lottie": "^1.2.10",
"@types/react-table": "7.7.20",
"@types/uuid": "^10.0.0",
Expand All @@ -123,4 +123,4 @@
"react-test-renderer": "^18.3.1",
"typescript-eslint": "^7.8.0"
}
}
}
Loading

0 comments on commit 8ee2590

Please sign in to comment.