Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Promote from preprod to prod #388

Merged
merged 2 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,23 @@
"formik": "2.4.2"
},
"dependencies": {
"@google-cloud/logging": "^9.6.0",
"@google-cloud/profiler": "^4.1.1",
"@testing-library/dom": "^8.3.0",
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^14.2.1",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.7.4",
"blaise-api-node-client": "git+https://github.com/ONSdigital/blaise-api-node-client",
"blaise-design-system-react-components": "git+https://github.com/ONSdigital/blaise-design-system-react-components#0.14.0",
"blaise-login-react": "git+https://github.com/ONSdigital/blaise-login-react#1.1.0",
"blaise-design-system-react-components": "git+https://github.com/ONSdigital/blaise-design-system-react-components#0.14.1",
"blaise-login-react": "git+https://github.com/ONSdigital/blaise-login-react#1.1.1",
"dotenv": "^10.0.0",
"ejs": "^3.1.10",
"express": "^4.19.2",
"formik": "2.4.2",
"history": "^4.9.0",
"jest-cucumber": "^3.0.0",
"jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21",
"multer": "^1.4.2",
"number-to-words": "^1.2.4",
Expand Down
125 changes: 0 additions & 125 deletions server/BlaiseAPI/index.ts

This file was deleted.

8 changes: 4 additions & 4 deletions server/index.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import GetNodeServer from "./server";
import pino from "pino";
import { loadConfigFromEnv } from "./Config";
import BlaiseApiClient from "blaise-api-node-client";
import { Auth } from "blaise-login-react/blaise-login-react-server";
import dotenv from "dotenv";
import createLogger from "./logger/pinoLogger";

const port: string = process.env.PORT || "5002";
const logger = pino();

if (process.env.NODE_ENV !== "production") {
dotenv.config();
}
const config = loadConfigFromEnv();
const blaiseApiClient = new BlaiseApiClient(config.BlaiseApiUrl);
const auth = new Auth(config);
const server = GetNodeServer(config, blaiseApiClient, auth);
const pinoLogger = createLogger();
const server = GetNodeServer(config, blaiseApiClient, auth, pinoLogger);
server.listen(port);

logger.info("App is listening on port " + port);
pinoLogger.logger.info("BAM nodejs server is listening on port " + port);
6 changes: 6 additions & 0 deletions server/interfaces/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface AuditLog {
id: string;
timestamp: string;
message: string;
severity: string;
}
1 change: 0 additions & 1 deletion server/interfaces/server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { AuthConfig } from "blaise-login-react/blaise-login-react-server";
import { string } from "prop-types";

export interface CustomConfig extends AuthConfig {
BlaiseApiUrl: string
Expand Down
59 changes: 59 additions & 0 deletions server/logger/cloudLogging.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { Logging } from "@google-cloud/logging";
import { IncomingMessage } from "http";
import { AuditLog } from "../interfaces/logger";

export function formatLogMessage(text: string): string {
const message = text.replace(/[^\x20-\x7E\r\n]+/g, "");
const logFormat = "AUDIT_LOG: message";
return logFormat.replace("message", message);
}

export default class AuditLogger {
projectId: string;
logger: Logging;
logName: string;

constructor(projectId: string) {
this.projectId = projectId;
this.logger = new Logging({ projectId: this.projectId });
this.logName = `projects/${this.projectId}/logs/stdout`;
}

info(logger: IncomingMessage["log"], message: string): void {
const log = formatLogMessage(message);
logger.info(log);
}

error(logger: IncomingMessage["log"], message: string): void {
const log = formatLogMessage(message);
logger.error(log);
}

async getLogs(): Promise<AuditLog[]> {
const auditLogs: AuditLog[] = [];
const log = this.logger.log(this.logName);
const [entries] = await log.getEntries({ filter: "jsonPayload.message=~\"^AUDIT_LOG: \"", maxResults: 50 });
for (const entry of entries) {
let id = "";
let timestamp = "";
let severity = "INFO";
if (entry.metadata.insertId != null) {
id = entry.metadata.insertId;
}
if (entry.metadata.timestamp != null) {
timestamp = entry.metadata.timestamp.toString();
}
if (entry.metadata.severity != null) {
severity = entry.metadata.severity.toString();
}
auditLogs.push({
id: id,
timestamp: timestamp,
message: entry.data.message.replace(/^AUDIT_LOG: /, ""),
severity: severity
});
}
return auditLogs;
}
}
File renamed without changes.
148 changes: 148 additions & 0 deletions server/routes/blaiseApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import express, { Request, Response, Router } from "express";
import { CustomConfig } from "../interfaces/server";
import { Auth } from "blaise-login-react/blaise-login-react-server";
import BlaiseApiClient from "blaise-api-node-client";
import AuditLogger from "../logger/cloudLogging";

export default function blaiseApi(config: CustomConfig, auth: Auth, blaiseApiClient: BlaiseApiClient, auditLogger: AuditLogger): Router {
const router = express.Router();

router.get("/api/roles", auth.Middleware, async function (req: Request, res: Response) {
res.status(200).json(await blaiseApiClient.getUserRoles());
});

router.get("/api/users", auth.Middleware, async function (req: Request, res: Response) {
res.status(200).json(await blaiseApiClient.getUsers());
});

router.patch("/api/users/:user/rolesAndPermissions", auth.Middleware, async function (req: Request, res: Response) {
const currentUser = auth.GetUser(auth.GetToken(req));
const { role } = req.body;
const user = req.params.user;
let newServerParks: string[];
let newDefaultServerPark: string;

if (!user || !role) {
return res.status(400).json("No user or role provided");
}

const roleServerParksOverride = config.RoleToServerParksMap[role];
if (roleServerParksOverride != null) {
newServerParks = roleServerParksOverride;
newDefaultServerPark = roleServerParksOverride[0];
} else {
const defaultServerPark = config.RoleToServerParksMap["DEFAULT"];
newServerParks = defaultServerPark;
newDefaultServerPark = defaultServerPark[0];
}

try {
await blaiseApiClient.changeUserRole(user, role);
await blaiseApiClient.changeUserServerParks(user, newServerParks, newDefaultServerPark);
const successMessage = `${currentUser.name || "Unknown user"} has successfully updated user role and permissions to ${role} for ${user}`;
auditLogger.info(req.log, successMessage);
return res.status(200).json({
message: "Successfully updated user role and permissions to " + role + " for " + user
});
} catch (error) {
const errorMessage = `Error whilst trying to update user role and permissions to ${role} for ${req.params.user}, with error message: ${error}`;
auditLogger.info(req.log, `${currentUser.name || "Unknown user"} has failed to update user role and permissions to ${role} for ${user}`);
auditLogger.error(req.log, errorMessage);
return res.status(500).json({
message: "Failed to update user role and permissions to " + role + " for " + user
});
}
});

router.get("/api/users/:user", auth.Middleware, async function (req: Request, res: Response) {
if (!req.params.user) {
return res.status(400).json("No user provided");
}

try {
const user = await blaiseApiClient.getUser(req.params.user);
const successMessage = `Successfully fetched user details for ${req.params.user}`;
return res.status(200).json({
message: successMessage,
data: user
});
} catch (error) {
const errorMessage = `Error whilst trying to retrieve user ${req.params.user}: ${error}`;
return res.status(500).json({
message: errorMessage,
error: error
});
}
});

router.get("/api/change-password/:user", auth.Middleware, async function (req: Request, res: Response) {
const currentUser = auth.GetUser(auth.GetToken(req));
let { password } = req.headers;

if (Array.isArray(password)) {
password = password.join("");
}

if (!req.params.user || !password) {
return res.status(400).json("No user or password provided");
}

blaiseApiClient.changePassword(req.params.user, password).then(() => {
auditLogger.info(req.log, `${currentUser.name || "Unknown"} has successfully changed the password for ${req.params.user}`);
return res.status(204).json(null);
}).catch((error: unknown) => {
auditLogger.info(req.log, `${currentUser.name || "Unknown"} has failed to change the password for ${req.params.user}`);
auditLogger.error(req.log, `Error whilst trying to change password for ${req.params.user}: ${error}`);
return res.status(500).json(error);
});
});

router.delete("/api/users", auth.Middleware, async function (req: Request, res: Response) {
try {
const currentUser = auth.GetUser(auth.GetToken(req));
let { user } = req.headers;

if (Array.isArray(user)) {
user = user.join("");
}

if (!user) {
auditLogger.error(req.log, "No user provided for deletion");
return res.status(400).json();
}
auditLogger.info(req.log, `${currentUser.name || "Unknown"} has successfully deleted user called ${user}`);
return res.status(204).json(await blaiseApiClient.deleteUser(user));
} catch (error) {
auditLogger.error(req.log, `Error whilst trying to delete user, ${req.headers.user}, with error message: ${error}`);
return res.status(500).json(error);
}
});

router.post("/api/users", auth.Middleware, async function (req: Request, res: Response) {
try {
const currentUser = auth.GetUser(auth.GetToken(req));
const data = req.body;

if(!data.role) {
return res.status(400).json({ message: "No role provided for user creation" });
}

const roleServerParksOverride = config.RoleToServerParksMap[data.role];
if (roleServerParksOverride != null) {
data.serverParks = roleServerParksOverride;
data.defaultServerPark = roleServerParksOverride[0];
} else {
const defaultServerPark = config.RoleToServerParksMap["DEFAULT"];
data.serverParks = defaultServerPark;
data.defaultServerPark = defaultServerPark[0];
}
auditLogger.info(req.log, `${currentUser.name || "Unknown"} has successfully created user, ${data.name}, with an assigned role of ${data.role}`);
return res.status(200).json(await blaiseApiClient.createUser(data));
} catch (error) {
auditLogger.error(req.log, `Error whilst trying to create new user, ${req.body.name}, with error message: ${error}`);
return res.status(500).json(error);
}
});

return router;
}
Loading
Loading