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 BLAIS5-4254 to main #386

Merged
merged 42 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
e04ceb9
build: use improved blaise-login components
kristian4res Oct 28, 2024
af4e2d0
wip: add logging
kristian4res Oct 28, 2024
44327f0
wip: add logging
kristian4res Oct 28, 2024
1824256
build: use latest blaise-login-react; use cloud logging (same as DQS …
kristian4res Oct 29, 2024
5f7d124
feat: add and configure gcp cloud logging
kristian4res Oct 29, 2024
3e16abf
fix: trim trailing whitespaces for name and pwd
kristian4res Oct 29, 2024
600ed11
wip: use pino http logger and cloud logging (test with BlaiseAPI)
kristian4res Oct 29, 2024
8cf5e3e
wip: log current user or requester
kristian4res Oct 29, 2024
7c9c1ec
wip: log current user or requester
kristian4res Oct 29, 2024
3759235
wip: log current user or requester
kristian4res Oct 29, 2024
e8723a6
feat: log current user initiating the action
kristian4res Oct 29, 2024
0909143
test: involve auth process in tests by using mock JWT values
kristian4res Oct 29, 2024
5ff600f
feat/refactor: add logging to create, delete and update/edit actions
kristian4res Oct 30, 2024
c7a4e00
refactor: restructure server folder
kristian4res Oct 30, 2024
e937804
test: include log asserts in unit tests
kristian4res Oct 30, 2024
df3c82e
chore: remove unused import
kristian4res Oct 31, 2024
a4ab55a
chore: remove unused import
kristian4res Oct 31, 2024
b376eb8
wip: fix unit test?
kristian4res Oct 31, 2024
7f12c98
wip: fix unit test?
kristian4res Oct 31, 2024
6ecd43c
wip: fix unit test?
kristian4res Oct 31, 2024
525b9cc
wip: fix deploy?
kristian4res Oct 31, 2024
0206e7c
wip: checking logs for roles and permissions
kristian4res Oct 31, 2024
98180f5
chore: remove audit logs endpoint
kristian4res Nov 4, 2024
a9d64b0
test: relocate blaise api tests
kristian4res Nov 4, 2024
f2791cc
refactor: trim whitespace in text user inputs; allow custom IDs for O…
kristian4res Nov 4, 2024
54e8685
test: trim whitespace in text user inputs; allow custom IDs for ONSPa…
kristian4res Nov 4, 2024
b72e261
refactor: assign correct serverparks based on role, before sending to…
kristian4res Nov 4, 2024
1b308a9
refactor: assign correct serverparks based on role, before sending to…
kristian4res Nov 4, 2024
b4b50ef
chore: remove console log
kristian4res Nov 4, 2024
f55a3ac
ref/test: improve logs and add error test case
kristian4res Nov 4, 2024
6a15aaa
ref: improve logs
kristian4res Nov 4, 2024
65e70da
ref: improve logs
kristian4res Nov 4, 2024
96d5950
ref: improve logs
kristian4res Nov 4, 2024
f6d4648
ref: improve logs
kristian4res Nov 4, 2024
fd20b67
ref: improve logs
kristian4res Nov 4, 2024
5a57a47
ref: improve logs
kristian4res Nov 4, 2024
0168d13
fix: sanitize message to prevent log injection
kristian4res Nov 5, 2024
736d33e
chore: remove unused method
kristian4res Nov 5, 2024
5580c25
chore: remove comments
kristian4res Nov 5, 2024
5252d26
build: update design system release reference
kristian4res Nov 5, 2024
fb143be
feat/test: sanitize log messages whilst preserving readability
kristian4res Nov 5, 2024
41721d5
ref: improve code
kristian4res Nov 5, 2024
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
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"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",
Expand All @@ -34,7 +35,7 @@
"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-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",
Expand Down
56 changes: 56 additions & 0 deletions server/AuditLogger/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Logging } from "@google-cloud/logging";
import { IncomingMessage } from "http";

export interface AuditLog {
id: string;
timestamp: string;
message: string;
severity: string;
}

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 {
logger.info(`AUDIT_LOG: ${message}`);
Fixed Show fixed Hide fixed
}

error(logger: IncomingMessage["log"], message: string): void {
logger.error(`AUDIT_LOG: ${message}`);
Fixed Show fixed Hide fixed
}

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;
}
}
23 changes: 17 additions & 6 deletions server/BlaiseAPI/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@
import { CustomConfig } from "../interfaces/server";
import { Auth } from "blaise-login-react/blaise-login-react-server";
import BlaiseApiClient from "blaise-api-node-client";
import createLogger from "../pino";
import AuditLogger from "../AuditLogger";

export default function BlaiseAPIRouter(config: CustomConfig, auth: Auth, blaiseApiClient: BlaiseApiClient): Router {
const pinoLogger = createLogger();
Fixed Show fixed Hide fixed

export default function BlaiseAPIRouter(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) {
auditLogger.info(req.log, `USER: ${req.headers.user} has successfully fetched all users`);
res.status(200).json(await blaiseApiClient.getUsers());
});

Expand Down Expand Up @@ -37,13 +42,13 @@
await blaiseApiClient.changeUserRole(user, role);
await blaiseApiClient.changeUserServerParks(user, newServerParks, newDefaultServerPark);
const successMessage = `Successfully updated user role and permissions to ${role} for ${user}`;
console.log(successMessage + ` at ${(new Date()).toLocaleTimeString("en-UK")} ${(new Date()).toLocaleDateString("en-UK")}`);
auditLogger.info(req.log, successMessage + ` at ${(new Date()).toLocaleTimeString("en-UK")} ${(new Date()).toLocaleDateString("en-UK")}`);
return res.status(200).json({
message: successMessage + " today at " + (new Date()).toLocaleTimeString("en-UK")
});
} catch (error) {
const errorMessage = `Error whilst trying to update user role and permissions to ${role} for ${req.params.user}: ${error}`;
console.error(errorMessage);
auditLogger.error(req.log, errorMessage + ` at ${(new Date()).toLocaleTimeString("en-UK")} ${(new Date()).toLocaleDateString("en-UK")}`);
return res.status(500).json({
message: errorMessage
});
Expand All @@ -58,13 +63,14 @@
try {
const user = await blaiseApiClient.getUser(req.params.user);
const successMessage = `Successfully fetched user details for ${req.params.user}`;
auditLogger.info(req.log, successMessage);
return res.status(200).json({
message: successMessage,
data: user
});
} catch (error) {
const errorMessage = `Error whilst trying to retrieve user ${req.params.user}: ${error}`;
console.error(errorMessage);
auditLogger.error(req.log, errorMessage);
return res.status(500).json({
message: errorMessage,
error: error
Expand All @@ -84,9 +90,10 @@
}

blaiseApiClient.changePassword(req.params.user, password).then(() => {
auditLogger.info(req.log, `Successfully changed password for ${req.params.user}`);
return res.status(204).json(null);
}).catch((error: unknown) => {
console.error(error);
auditLogger.error(req.log, `Error whilst trying to change password for ${req.params.user}: ${error}`);
return res.status(500).json(error);
});
});
Expand All @@ -99,15 +106,17 @@
}

if (!user) {
auditLogger.error(req.log, "No user provided for deletion");
return res.status(400).json();
}
auditLogger.info(req.log, `Successfully deleted user: ${user}`);
return res.status(204).json(await blaiseApiClient.deleteUser(user));
});

router.post("/api/users", auth.Middleware, async function (req: Request, res: Response) {
const data = req.body;
if(!data.role){
return res.status(400).json();
return res.status(400).json({ message: "No role provided for user creation" });
}
const roleServerParksOverride = config.RoleToServerParksMap[data.role];
if (roleServerParksOverride != null) {
Expand All @@ -118,6 +127,8 @@
data.serverParks = defaultServerPark;
data.defaultServerPark = defaultServerPark[0];
}
const currentUser = req.headers.user;
auditLogger.info(req.log, `${currentUser} has created user: ${data.username}`);
return res.status(200).json(await blaiseApiClient.createUser(data));
});

Expand Down
22 changes: 11 additions & 11 deletions server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,45 +7,43 @@ import BlaiseAPIRouter from "./BlaiseAPI";
import multer from "multer";
import * as profiler from "@google-cloud/profiler";
import { newLoginHandler, Auth } from "blaise-login-react/blaise-login-react-server";
import pino from "pino";
import { CustomConfig } from "./interfaces/server";
import BlaiseApi from "blaise-api-node-client";
import { Express } from "express";
import fs from "fs";
import AuditLogger from "./AuditLogger";

export default function GetNodeServer(config: CustomConfig, blaiseApi: BlaiseApi, auth: Auth): Express
{
const pinoLogger = pino();
const pinoLogger = createLogger();
profiler.start({ logLevel: 4 }).catch((err: unknown) => {
pinoLogger.error(`Failed to start profiler: ${err}`);
pinoLogger.logger.error(`Failed to start profiler: ${err}`);
});

const upload = multer();

const server = express();
const logger = createLogger();

server.use(upload.any());

axios.defaults.timeout = 10000;

const logger = createLogger();
server.use(logger);
axios.defaults.timeout = 10000;

// where ever the react built package is
const buildFolder = "../build";

const auditLogger = new AuditLogger(config.ProjectId);
const loginHandler = newLoginHandler(auth, blaiseApi);

// Health Check endpoint
server.get("/bam-ui/:version/health", async function (req: Request, res: Response) {
pinoLogger.info("Heath Check endpoint called");
auditLogger.info(req.log, "Heath Check endpoint called");
req.log.info("Heath Check endpoint called");
res.status(200).json({ healthy: true });
});

server.use("/", loginHandler);

// All Endpoints calling the Blaise API
server.use("/", BlaiseAPIRouter(config, auth, blaiseApi));
server.use("/", BlaiseAPIRouter(config, auth, blaiseApi, auditLogger));

// treat the index.html as a template and substitute the values at runtime
server.set("views", path.join(__dirname, "/views"));
Expand All @@ -66,8 +64,10 @@ export default function GetNodeServer(config: CustomConfig, blaiseApi: BlaiseApi

server.use(function (err, _req, res, _next) {
if (err && err.stack) {
auditLogger.error(res, err.stack);
console.error(err.stack);
} else {
auditLogger.error(res, "An undefined error occurred");
console.error("An undefined error occurred");
}
res.render("../views/500.html", {});
Expand Down
5 changes: 3 additions & 2 deletions src/pages/users/UserUpload/NewUser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ function NewUserComponent(): ReactElement {
setUsername(formData.username);
if (formData.username && formData.password) {
const newUser: NewUser = {
name: formData.username,
password: formData.password,
name: formData.username.trim(),
password: formData.password.trim(),
role: role,
// TODO: Are these needed? These should be set in the server depending on the role
defaultServerPark: "gusty",
serverParks: ["gusty"]
};
Expand Down
25 changes: 23 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3171,6 +3171,27 @@
stream-events "^1.0.5"
uuid "^8.0.0"

"@google-cloud/logging@^9.6.0":
version "9.9.0"
resolved "https://registry.yarnpkg.com/@google-cloud/logging/-/logging-9.9.0.tgz#d13fd6ae359e02123809b2bd640a0a8f71793434"
integrity sha512-rJQ0i9COI1WbtWuGyXL8edF4OA3XHcvgAq5I2DZmjKFvmCLCcspFIWZtztxgd0UgmrrshQVZ0R76oBHjTGggkg==
dependencies:
"@google-cloud/common" "^3.4.1"
"@google-cloud/paginator" "^3.0.0"
"@google-cloud/projectify" "^2.0.0"
"@google-cloud/promisify" "^2.0.0"
arrify "^2.0.1"
dot-prop "^6.0.0"
eventid "^2.0.0"
extend "^3.0.2"
gcp-metadata "^4.0.0"
google-auth-library "^7.0.0"
google-gax "^2.24.1"
on-finished "^2.3.0"
pumpify "^2.0.1"
stream-events "^1.0.5"
uuid "^8.0.0"

"@google-cloud/paginator@^3.0.0":
version "3.0.7"
resolved "https://registry.yarnpkg.com/@google-cloud/paginator/-/paginator-3.0.7.tgz#fb6f8e24ec841f99defaebf62c75c2e744dd419b"
Expand Down Expand Up @@ -6296,9 +6317,9 @@ bl@^4.0.3, bl@^4.1.0:
path-parse "^1.0.7"
typescript "~4.2.2"

"blaise-login-react@git+https://github.com/ONSdigital/blaise-login-react#1.1.0":
"blaise-login-react@git+https://github.com/ONSdigital/blaise-login-react#1.1.1":
version "0.0.0"
resolved "git+https://github.com/ONSdigital/blaise-login-react#39f4182a62b1faaf01039fb8c94adc296ac78766"
resolved "git+https://github.com/ONSdigital/blaise-login-react#f7fc532e1938ccb45831816311c85336510383ae"

bluebird@^3.5.5:
version "3.7.2"
Expand Down
Loading