-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #387 from ONSdigital/main
Promote from main to preprod
- Loading branch information
Showing
19 changed files
with
833 additions
and
453 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
Oops, something went wrong.