Skip to content

Commit

Permalink
#000: Resolve merge conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
Manjunath authored and Manjunath committed Dec 2, 2024
2 parents 996ae2b + fe26888 commit 1b4729a
Show file tree
Hide file tree
Showing 170 changed files with 721 additions and 2,419 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"json-difference": "^1.16.1",
"jsonwebtoken": "^9.0.1",
"kafkajs": "^2.2.4",
"keycloak-connect": "^26.0.5",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"morgan": "^1.10.0",
Expand Down
26 changes: 24 additions & 2 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import mountProxies from './main/utils/proxy';
import passport from 'passport';
import { pool } from './shared/databases/postgres';
import pgSession from 'connect-pg-simple';
import { authProviderFactory } from './main/services/authProviderFactory';
import path from 'path';

const app = express();
const sessionSecret: any = process.env.SESSION_SECRET
Expand All @@ -27,9 +29,29 @@ mountProxies(app, proxies);
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(passport.initialize());
app.use(passport.session());
import './main/services/auth'; // Don't change this import position in file
import appConfig from './shared/resources/appConfig';

const authenticationType = appConfig.AUTHENTICATION_TYPE;
const keycloakUrl = appConfig.KEYCLOAK.URL;
const keycloakClientID = appConfig.KEYCLOAK.CLIENT_ID;
const keycloakRealm = appConfig.KEYCLOAK.REALM;

const keycloakConfig = {
resource: keycloakClientID,
realm: keycloakRealm,
authServerUrl: keycloakUrl,
bearerOnly: false
};

const authProvider = authProviderFactory(authenticationType,keycloakConfig, sessionStore);
app.use(authProvider.init())
app.get('/console/logout', authProvider.authenticate(), async (req:any, res) => {
await authProvider.logout(req, res);
res.redirect('/console');
});
app.get('/console', authProvider.authenticate(), (req, res) => {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

export default app;
3 changes: 2 additions & 1 deletion src/main/controllers/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ export default {
"transformationFieldMaxLen":100,
"denormInputFieldMaxLen":100,
"alertsPerPage":10
}
},
"AUTHENTICATION_TYPE": appConfig.AUTHENTICATION_TYPE
})
},
};
25 changes: 13 additions & 12 deletions src/main/controllers/user_create.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
import { Request, Response, NextFunction } from 'express';
import userService from '../services/oauthUsers';
import { v4 } from 'uuid';
import bcrypt from 'bcryptjs';
import { transform } from '../../shared/utils/transformResponse';
import _ from 'lodash';
import appConfig from '../../shared/resources/appConfig';
import { userCreateWithKeycloak } from '../services/keycloak';
import { userCreateAsBasic } from '../services/basic';

const authenticationType = appConfig.AUTHENTICATION_TYPE;

export default {
name: 'user:create',
handler: () => async (req: Request, res: Response, next: NextFunction) => {
try {
const userRequest = _.get(req, ['body', 'request']);
userRequest.user_name = userRequest.user_name.trim().replace(/\s+/g, '_');
const { password } = userRequest;
userRequest.password = await bcrypt.hash(password, 12);
if (userRequest.mobile_number) {
const { country_code, number } = userRequest.mobile_number;
userRequest.mobile_number = `${String(country_code).trim()}_${String(number).trim()}`;
if (authenticationType === 'keycloak') {
const keycloakToken = JSON.parse(req?.session['keycloak-token']);
const access_token = keycloakToken.access_token;
const result = await userCreateWithKeycloak(access_token, userRequest);
res.status(200).json(transform({ id: req.body.id, result: { id: result.id, user_name: result.user_name, email_address: result.email_address } }));
} else if (authenticationType === 'basic') {
const result = await userCreateAsBasic(userRequest);
res.status(200).json(transform({ id: req.body.id, result: { id: result.id, user_name: result.user_name, email_address: result.email_address } }));
}
const userIdentifier = { id: v4(), created_on: new Date().toISOString() };
const userInfo = { ...userRequest, ...userIdentifier };
const result = await userService.save(userInfo);
res.status(200).json(transform({ id: req.body.id, result: { id: result.id, user_name: result.user_name, email_address: result.email_address } }));
} catch (error) {
next(error);
}
Expand Down
29 changes: 27 additions & 2 deletions src/main/controllers/user_read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,38 @@ import { NextFunction, Request, Response } from 'express';
import _ from 'lodash';
import userService from '../services/oauthUsers';
import { transform } from '../../shared/utils/transformResponse';
import appConfig from '../../shared/resources/appConfig';

const authenticationType = appConfig.AUTHENTICATION_TYPE;

const getUserDetails = function (request: Request) {
if (authenticationType === 'basic') {
const token = _.get(request, ['session', 'token']);
const userName = _.get(request, ['session', 'userDetails', 'user_name']);
const userDetails = {
token: token,
sessionUserName: userName,
};
return userDetails;
} else if (authenticationType === 'keycloak') {
const keycloakToken = JSON.parse(request?.session['keycloak-token']);
const access_token = keycloakToken?.access_token;
const preferred_username = request?.session?.preferred_username;
const userDetails = {
token: access_token,
sessionUserName: preferred_username,
};
return userDetails;
}
};

export default {
name: 'user:read',
handler: () => async (request: Request, response: Response, next: NextFunction) => {
try {
const { user_name } = _.get(request, ['params']);
const sessionUserName = _.get(request, ['session', 'userDetails', 'user_name']);
const sessionUserDetails = getUserDetails(request);
const sessionUserName = sessionUserDetails?.sessionUserName;
if (user_name !== sessionUserName) {
response.status(403).json(
transform({
Expand All @@ -29,7 +54,7 @@ export default {
const { fields } = _.get(request, ['query']);
const includeToken = _.toLower(_.toString(fields)) === 'user_token';
if (includeToken) {
responseData.result.token = _.get(request, ['session', 'token']);
responseData.result.token = sessionUserDetails?.token;
}
response.status(200).json(transform(responseData));
} catch (error) {
Expand Down
31 changes: 22 additions & 9 deletions src/main/helpers/oauth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import appConfig from "../../../shared/resources/appConfig";

const server = oauth2orize.createServer();
const baseURL = appConfig.BASE_URL;
const authenticationType = appConfig.AUTHENTICATION_TYPE;

server.serializeClient((client, done) => {
return done(null, client.id)
Expand Down Expand Up @@ -165,14 +166,26 @@ export const token = [
];

export const ensureLoggedInMiddleware = (request: Request, response: Response, next: NextFunction) => {
if (!request?.session?.passport?.user) {
const errorObj = {
status: 401,
message: "You don't have access to view this resource",
responseCode: 'UNAUTHORIZED',
errorCode: 'UNAUTHORIZED',
};
return next(errorObj)
if(authenticationType === 'basic'){
if (!request?.session?.passport?.user) {
const errorObj = {
status: 401,
message: "You don't have access to view this resource",
responseCode: 'UNAUTHORIZED',
errorCode: 'UNAUTHORIZED',
};
return next(errorObj);
}
} else if(authenticationType === 'keycloak'){
if (!request?.session['keycloak-token']) {
const errorObj = {
status: 401,
message: "You don't have access to view this resource",
responseCode: 'UNAUTHORIZED',
errorCode: 'UNAUTHORIZED',
};
return next(errorObj);
}
}
return next();
}
};
13 changes: 11 additions & 2 deletions src/main/helpers/proxy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
/* eslint-disable prettier/prettier */
import { Request, Response } from "express";
import _ from 'lodash'
import { incrementApiCalls, incrementFailedApiCalls, setQueryResponseTime } from "./prometheus";
import appConfig from "../../shared/resources/appConfig";

const authenticationType = appConfig.AUTHENTICATION_TYPE;

export const onError = ({ entity }: any) => (err: any, req: Request, res: Response) => {
incrementFailedApiCalls({ entity, endpoint: req.url });
Expand All @@ -19,9 +23,14 @@ 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;
const jwtToken: string = req.session?.token;
if (jwtToken) {
if(authenticationType === 'keycloak'){
const keycloakToken = JSON.parse(req?.session['keycloak-token']);
const access_token: string = keycloakToken.access_token;
proxyReq.setHeader('x-user-token', `${access_token}`);
}else if(authenticationType === 'basic'){
const jwtToken: string = req.session?.token;
proxyReq.setHeader('x-user-token', `${jwtToken}`);

}
incrementApiCalls({ entity, endpoint: req.url });
}
7 changes: 0 additions & 7 deletions src/main/resources/routesConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,6 @@ export default [
method: 'POST',
middlewares: [passportAuthenticateCallback.handler()],
},
{
path: 'logout',
method: 'GET',
middlewares: [
controllers.get('auth:logout')?.handler({}),
],
},
{
path: 'authorize',
method: 'GET',
Expand Down
15 changes: 15 additions & 0 deletions src/main/services/authProviderFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { PassportAuthProvider } from './passportAuthProvider';
import { KeycloakAuthProvider } from './keycloakAuthProvider';
import { BaseAuthProvider } from '../types';

export const authProviderFactory: (type: string, config?: any, sessionStore?: any) => BaseAuthProvider = (type, config, sessionStore) => {
switch (type) {
case 'keycloak':
return new KeycloakAuthProvider(config, sessionStore);
case 'basic':
return new PassportAuthProvider();
default:
throw new Error("Invalid authentication service type");
}
};
export { BaseAuthProvider };
17 changes: 17 additions & 0 deletions src/main/services/basic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import _ from 'lodash';
import { v4 } from 'uuid';
import bcrypt from 'bcryptjs';
import userService from '../services/oauthUsers';

export const userCreateAsBasic = async (userRequest: any) => {
const { password } = userRequest;
userRequest.password = await bcrypt.hash(password, 12);
if (userRequest.mobile_number) {
const { country_code, number } = userRequest.mobile_number;
userRequest.mobile_number = `${String(country_code).trim()}_${String(number).trim()}`;
}
const userIdentifier = { id: v4(), created_on: new Date().toISOString() };
const userInfo = { ...userRequest, ...userIdentifier };
const result = await userService.save(userInfo);
return result;
};
106 changes: 106 additions & 0 deletions src/main/services/keycloak.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import axios from 'axios';
import appConfig from '../../shared/resources/appConfig';
import _ from 'lodash';
import userService from '../services/oauthUsers';

const keycloakUrl = appConfig.KEYCLOAK.URL;
const keycloakHTTPClient = axios.create({ baseURL: keycloakUrl });
const keycloakRealm = appConfig.KEYCLOAK.REALM;

export const authenticated = async (request: any) => {
try {
const userId = request?.kauth?.grant?.access_token?.content?.sub?.split(':');
const email_address = request?.kauth?.grant?.access_token?.content?.email;
const preferred_username = request?.kauth?.grant?.access_token?.content?.preferred_username;

request.session.userId = userId?.[userId.length - 1];
request.session.email_address = email_address;
request.session.preferred_username = preferred_username;

const user = await userService.find({ id: userId?.[0] });
request.session.roles = user?.roles;
} catch (err) {
console.log('user not authenticated', request?.kauth?.grant?.access_token?.content?.sub, err);
}
};

export const deauthenticated = function (request: any) {
delete request?.session?.roles;
delete request?.session?.userId;
delete request?.session?.email_address;
delete request?.session?.preferred_username;
delete request?.session?.auth_redirect_uri;
delete request?.session?.['keycloak-token'];

if (request?.session) {
request.session.sessionEvents = request?.session?.sessionEvents || [];
delete request?.session?.sessionEvents;
}
};

export const userCreate = async (access_token: any, userRequest: any) => {
const { user_name, email_address } = userRequest;
const password = _.trim(userRequest.password);
const payload = {
email: email_address,
username: user_name,
enabled: true,
credentials: [
{
type: 'password',
value: password,
temporary: false,
},
],
};

return keycloakHTTPClient
.post(`/admin/realms/${keycloakRealm}/users`, payload, {
headers: {
Authorization: `Bearer ${access_token}`,
},
})
.then((response) => {
const location = _.get(response, 'headers.location');
const userId = location ? _.last(location.split('/')) : null;
console.log('keyuser', userId);
if (!userId) {
throw new Error('UserId not found');
}
return userId;
})
.catch((error) => {
console.log(error);
});
};

export const userCreateWithKeycloak = async (access_token: any, userRequest: any) => {
const { user_name, email_address, roles } = userRequest;
const id = await userCreate(access_token, userRequest);
const created_on = new Date().toISOString();
const userInfo = { id, user_name, email_address, created_on, roles };
const result = await userService.save(userInfo);
return result;
};

export const keycloakLogout = async (req: any) => {
const userId = req?.session?.userId;
const access_token = req?.kauth?.grant?.access_token?.token;
const refresh_token = req?.kauth?.grant?.refresh_token?.token;

const data = new URLSearchParams({
client_id: req?.kauth?.grant?.access_token?.clientId,
refresh_token: refresh_token,
});

return keycloakHTTPClient
.post(`admin/realms/${keycloakRealm}/users/${userId}/logout`, data, {
headers: {
Authorization: `Bearer ${access_token}`,
},
})
.then()
.catch((error) => {
console.log(error);
});
};
Loading

0 comments on commit 1b4729a

Please sign in to comment.