diff --git a/api/Controllers/Collection.js b/api/Controllers/Collection.js index 257e6cb0..d275a917 100644 --- a/api/Controllers/Collection.js +++ b/api/Controllers/Collection.js @@ -22,7 +22,9 @@ module.exports.getCollectionBasicList = async function getCollectionBasicList(re module.exports.getCollections = async function getCollections(req, res, next) { try { - const getCollections = await collectionService.getCollections(req.params.userName); + const userId = req.userObject.userId; + const elevate = req.query.elevate; + const getCollections = await collectionService.getCollections(userId, elevate); if (getCollections.error) { res.status(500).json({ error: 'Internal Server Error', detail: getCollections.error }); } else { diff --git a/api/Controllers/Marketplace.js b/api/Controllers/Marketplace.js index 3a2dc38e..41f74efc 100644 --- a/api/Controllers/Marketplace.js +++ b/api/Controllers/Marketplace.js @@ -35,7 +35,7 @@ module.exports.purchaseTheme = async function purchaseTheme(req, res, next) { module.exports.getUserThemes = async function getUserThemes(req, res, next) { try { - const userId = req.params.userId; + const userId = req.userObject.userId; const themes = await marketplaceService.getUserThemes(userId); res.status(200).json(themes); } catch (error) { @@ -45,7 +45,7 @@ module.exports.getUserThemes = async function getUserThemes(req, res, next) { module.exports.getUserPoints = async function getUserPoints(req, res, next) { try { - const userId = req.params.userId; + const userId = req.userObject.userId; const points = await marketplaceService.getUserPoints(userId); res.status(200).json(points); } catch (error) { diff --git a/api/Controllers/Metrics.js b/api/Controllers/Metrics.js index 1227c87c..49959b07 100644 --- a/api/Controllers/Metrics.js +++ b/api/Controllers/Metrics.js @@ -12,7 +12,8 @@ const metricsService = require('../Services/metricsService'); module.exports.getCollectionAssetLabel = async function getCollectionAssetLabel(req, res, next) { try { - const getMetrics = await metricsService.getCollectionAssetLabel(req, res, next); + const collectionId = req.params.collectionId; + const getMetrics = await metricsService.getCollectionAssetLabel(collectionId); if (getMetrics) { res.status(200).json(getMetrics); } else { @@ -25,7 +26,8 @@ module.exports.getCollectionAssetLabel = async function getCollectionAssetLabel( module.exports.getCollectionPoamLabel = async function getCollectionPoamLabel(req, res, next) { try { - const getMetrics = await metricsService.getCollectionPoamLabel(req, res, next); + const collectionId = req.params.collectionId; + const getMetrics = await metricsService.getCollectionPoamLabel(collectionId); if (getMetrics) { res.status(200).json(getMetrics); } else { @@ -38,7 +40,8 @@ module.exports.getCollectionPoamLabel = async function getCollectionPoamLabel(re module.exports.getCollectionPoamStatus = async function getCollectionPoamStatus(req, res, next) { try { - const getMetrics = await metricsService.getCollectionPoamStatus(req, res, next); + const collectionId = req.params.collectionId; + const getMetrics = await metricsService.getCollectionPoamStatus(collectionId); if (getMetrics && getMetrics.poamStatus) { res.status(200).json(getMetrics); } else { @@ -51,7 +54,8 @@ module.exports.getCollectionPoamStatus = async function getCollectionPoamStatus( module.exports.getCollectionPoamSeverity = async function getCollectionPoamSeverity(req, res, next) { try { - const getMetrics = await metricsService.getCollectionPoamSeverity(req, res, next); + const collectionId = req.params.collectionId; + const getMetrics = await metricsService.getCollectionPoamSeverity(collectionId); if (getMetrics && getMetrics.poamSeverity) { res.status(200).json(getMetrics); } else { @@ -64,7 +68,8 @@ module.exports.getCollectionPoamSeverity = async function getCollectionPoamSever module.exports.getCollectionPoamScheduledCompletion = async function getCollectionPoamScheduledCompletion(req, res, next) { try { - const getMetrics = await metricsService.getCollectionPoamScheduledCompletion(req, res, next); + const collectionId = req.params.collectionId; + const getMetrics = await metricsService.getCollectionPoamScheduledCompletion(collectionId); if (getMetrics && getMetrics.poamScheduledCompletion) { res.status(200).json(getMetrics); } else { @@ -77,7 +82,8 @@ module.exports.getCollectionPoamScheduledCompletion = async function getCollecti module.exports.getCollectionMonthlyPoamStatus = async function getCollectionMonthlyPoamStatus(req, res, next) { try { - const getMetrics = await metricsService.getCollectionMonthlyPoamStatus(req, res, next); + const collectionId = req.params.collectionId; + const getMetrics = await metricsService.getCollectionMonthlyPoamStatus(collectionId); if (getMetrics && getMetrics.poamStatus) { res.status(200).json(getMetrics); } else { @@ -90,7 +96,8 @@ module.exports.getCollectionMonthlyPoamStatus = async function getCollectionMont module.exports.getAvailableAssetLabel = async function getAvailableAssetLabel(req, res, next) { try { - const getMetrics = await metricsService.getAvailableAssetLabel(req, res, next); + const userId = req.userObject.userId; + const getMetrics = await metricsService.getAvailableAssetLabel(userId); if (getMetrics) { res.status(200).json(getMetrics); } else { @@ -103,7 +110,8 @@ module.exports.getAvailableAssetLabel = async function getAvailableAssetLabel(re module.exports.getAvailablePoamLabel = async function getAvailablePoamLabel(req, res, next) { try { - const getMetrics = await metricsService.getAvailablePoamLabel(req, res, next); + const userId = req.userObject.userId; + const getMetrics = await metricsService.getAvailablePoamLabel(userId); if (getMetrics) { res.status(200).json(getMetrics); } else { @@ -116,7 +124,8 @@ module.exports.getAvailablePoamLabel = async function getAvailablePoamLabel(req, module.exports.getAvailableCollectionPoamCounts = async function getAvailableCollectionPoamCounts(req, res, next) { try { - const getMetrics = await metricsService.getAvailableCollectionPoamCounts(req, res, next); + const userId = req.userObject.userId; + const getMetrics = await metricsService.getAvailableCollectionPoamCounts(userId); if (getMetrics) { res.status(200).json(getMetrics); } else { @@ -129,7 +138,8 @@ module.exports.getAvailableCollectionPoamCounts = async function getAvailableCol module.exports.getAvailablePoamStatus = async function getAvailablePoamStatus(req, res, next) { try { - const getMetrics = await metricsService.getAvailablePoamStatus(req, res, next); + const userId = req.userObject.userId; + const getMetrics = await metricsService.getAvailablePoamStatus(userId); if (getMetrics && getMetrics.poamStatus) { res.status(200).json(getMetrics); } else { @@ -142,7 +152,8 @@ module.exports.getAvailablePoamStatus = async function getAvailablePoamStatus(re module.exports.getAvailablePoamSeverity = async function getAvailablePoamSeverity(req, res, next) { try { - const getMetrics = await metricsService.getAvailablePoamSeverity(req, res, next); + const userId = req.userObject.userId; + const getMetrics = await metricsService.getAvailablePoamSeverity(userId); if (getMetrics && getMetrics.poamSeverity) { res.status(200).json(getMetrics); } else { @@ -155,7 +166,8 @@ module.exports.getAvailablePoamSeverity = async function getAvailablePoamSeverit module.exports.getAvailableMonthlyPoamSeverity = async function getAvailableMonthlyPoamSeverity(req, res, next) { try { - const getMetrics = await metricsService.getAvailableMonthlyPoamSeverity(req, res, next); + const userId = req.userObject.userId; + const getMetrics = await metricsService.getAvailableMonthlyPoamSeverity(userId); if (getMetrics && getMetrics.poamSeverity) { res.status(200).json(getMetrics); } else { @@ -168,7 +180,8 @@ module.exports.getAvailableMonthlyPoamSeverity = async function getAvailableMont module.exports.getAvailableMonthlyPoamStatus = async function getAvailableMonthlyPoamStatus(req, res, next) { try { - const getMetrics = await metricsService.getAvailableMonthlyPoamStatus(req, res, next); + const userId = req.userObject.userId; + const getMetrics = await metricsService.getAvailableMonthlyPoamStatus(userId); if (getMetrics && getMetrics.poamStatus) { res.status(200).json(getMetrics); } else { @@ -181,7 +194,8 @@ module.exports.getAvailableMonthlyPoamStatus = async function getAvailableMonthl module.exports.getAvailablePoamScheduledCompletion = async function getAvailablePoamScheduledCompletion(req, res, next) { try { - const getMetrics = await metricsService.getAvailablePoamScheduledCompletion(req, res, next); + const userId = req.userObject.userId; + const getMetrics = await metricsService.getAvailablePoamScheduledCompletion(userId); if (getMetrics && getMetrics.poamScheduledCompletion) { res.status(200).json(getMetrics); } else { diff --git a/api/Controllers/Notification.js b/api/Controllers/Notification.js index 9eb1a491..fc645ac8 100644 --- a/api/Controllers/Notification.js +++ b/api/Controllers/Notification.js @@ -10,40 +10,45 @@ const notificationService = require('../Services/notificationService'); -module.exports.getAllNotificationsByUserId = async function getAllNotificationsByUserId(req, res, next) { +module.exports.getAllNotifications = async function getAllNotifications(req, res, next) { try { - const userId = req.params.userId; - const notifications = await notificationService.getAllNotificationsByUserId(userId); + const userId = req.userObject.userId; + const notifications = await notificationService.getAllNotifications(userId); + res.status(200).json(notifications); } catch (error) { res.status(500).json({ error: 'Internal Server Error', detail: error.message }); } }; -module.exports.getUnreadNotificationsByUserId = async function getUnreadNotificationsByUserId(req, res, next) { +module.exports.getUnreadNotifications = async function getUnreadNotifications(req, res, next) { try { - const userId = req.params.userId; - const notifications = await notificationService.getUnreadNotificationsByUserId(userId); + const userId = req.userObject.userId; + const notifications = await notificationService.getUnreadNotifications(userId); + res.status(200).json(notifications); } catch (error) { res.status(500).json({ error: 'Internal Server Error', detail: error.message }); } }; -module.exports.getUnreadNotificationCountByUserId = async function getUnreadNotificationCountByUserId(req, res, next) { +module.exports.getUnreadNotificationCount = async function getUnreadNotificationCount(req, res, next) { try { - const userId = req.params.userId; - const notificationCount = await notificationService.getUnreadNotificationCountByUserId(userId); + const userId = req.userObject.userId; + const notificationCount = await notificationService.getUnreadNotificationCount(userId); + res.status(200).json(notificationCount); } catch (error) { res.status(500).json({ error: 'Internal Server Error', detail: error.message }); } }; -module.exports.dismissNotificationByNotificationId = async function dismissNotificationByNotificationId(req, res, next) { +module.exports.dismissNotification = async function dismissNotification(req, res, next) { try { + const userId = req.userObject.userId; const notificationId = req.params.notificationId; - const unreadNotifications = await notificationService.dismissNotificationByNotificationId(notificationId); + const unreadNotifications = await notificationService.dismissNotification(userId, notificationId); + if (unreadNotifications !== null) { res.status(200).json(unreadNotifications); } else { @@ -54,10 +59,10 @@ module.exports.dismissNotificationByNotificationId = async function dismissNotif } }; -module.exports.dismissAllNotificationsByUserId = async function dismissAllNotificationsByUserId(req, res, next) { +module.exports.dismissAllNotifications = async function dismissAllNotifications(req, res, next) { try { - const userId = req.params.userId; - const dismissed = await notificationService.dismissAllNotificationsByUserId(userId); + const userId = req.userObject.userId; + const dismissed = await notificationService.dismissAllNotifications(userId); if (dismissed) { res.status(204).json(); } else { @@ -68,10 +73,11 @@ module.exports.dismissAllNotificationsByUserId = async function dismissAllNotifi } }; -module.exports.deleteNotificationByNotificationId = async function deleteNotificationByNotificationId(req, res, next) { +module.exports.deleteNotification = async function deleteNotification(req, res, next) { try { + const userId = req.userObject.userId; const notificationId = req.params.notificationId; - const deleted = await notificationService.deleteNotificationByNotificationId(notificationId); + const deleted = await notificationService.deleteNotification(userId, notificationId); if (deleted) { res.status(204).send(); @@ -83,10 +89,10 @@ module.exports.deleteNotificationByNotificationId = async function deleteNotific } }; -module.exports.deleteAllNotificationsByUserId = async function deleteAllNotificationsByUserId(req, res, next) { +module.exports.deleteAllNotifications = async function deleteAllNotifications(req, res, next) { try { - const userId = req.params.userId; - const deleted = await notificationService.deleteAllNotificationsByUserId(userId); + const userId = req.userObject.userId; + const deleted = await notificationService.deleteAllNotifications(userId); if (deleted) { res.status(204).send(); diff --git a/api/Controllers/Permissions.js b/api/Controllers/Permissions.js index e567b54c..5470c02c 100644 --- a/api/Controllers/Permissions.js +++ b/api/Controllers/Permissions.js @@ -10,18 +10,6 @@ const permissionService = require('../Services/permissionsService'); -module.exports.getCollectionPermission = async function getCollectionPermission(req, res, next) { - try { - const getCollection = await permissionService.getCollectionPermission(req.params.userName, req.params.collectionId, req, res, next); - if (getCollection) { - res.status(200).json(getCollection); - } else { - res.status(204).send(); - } - } catch (error) { - res.status(500).json({ error: 'Internal Server Error', detail: error.message }); - } -}; module.exports.getCollectionPermissions = async function getCollectionPermissions(req, res, next) { try { @@ -38,7 +26,9 @@ module.exports.getCollectionPermissions = async function getCollectionPermission module.exports.postPermission = async function postPermission(req, res, next) { try { - const permission = await permissionService.postPermission(req, res, next); + const userId = req.userObject.userId; + const elevate = req.query.elevate; + const permission = await permissionService.postPermission(userId, elevate, req); res.status(201).json(permission); } catch (error) { if (error.status === 400) { @@ -51,7 +41,9 @@ module.exports.postPermission = async function postPermission(req, res, next) { module.exports.putPermission = async function putPermission(req, res, next) { try { - const permission = await permissionService.putPermission(req, res, next); + const userId = req.userObject.userId; + const elevate = req.query.elevate; + const permission = await permissionService.putPermission(userId, elevate, req); res.status(200).json(permission); } catch (error) { if (error.status === 400) { @@ -64,7 +56,9 @@ module.exports.putPermission = async function putPermission(req, res, next) { module.exports.deletePermission = async function deletePermission(req, res, next) { try { - await permissionService.deletePermission(req, res, next); + const userId = req.userObject.userId; + const elevate = req.query.elevate; + await permissionService.deletePermission(userId, elevate, req); res.status(204).send(); } catch (error) { if (error.status === 400) { diff --git a/api/Controllers/Poam.js b/api/Controllers/Poam.js index 60caffc4..97093005 100644 --- a/api/Controllers/Poam.js +++ b/api/Controllers/Poam.js @@ -12,7 +12,8 @@ const poamService = require('../Services/poamService'); module.exports.getAvailablePoams = async function getAvailablePoams(req, res, next) { try { - const poams = await poamService.getAvailablePoams(req, res, next); + const userId = req.userObject.userId; + const poams = await poamService.getAvailablePoams(userId, req); res.status(200).json(poams); } catch (error) { if (error.status === 400) { diff --git a/api/Controllers/PoamApprover.js b/api/Controllers/PoamApprover.js index ab80ba08..506bea3b 100644 --- a/api/Controllers/PoamApprover.js +++ b/api/Controllers/PoamApprover.js @@ -28,24 +28,6 @@ module.exports.getPoamApproversByCollection = async function getPoamApproversByC } }; -module.exports.getPoamApproversByCollectionUser = async function getPoamApproversByCollectionUser(req, res, next) { - try { - const poamApprovers = await poamApproverService.getPoamApproversByCollectionUser(req, res, next); - res.status(200).json(poamApprovers); - } catch (error) { - res.status(400).json({ error: error.message }); - } -}; - -module.exports.getPoamApproversByUserId = async function getPoamApproversByUserId(req, res, next) { - try { - const poamApprovers = await poamApproverService.getPoamApproversByUserId(req, res, next); - res.status(200).json(poamApprovers); - } catch (error) { - res.status(400).json({ error: error.message }); - } -}; - module.exports.postPoamApprover = async function postPoamApprover(req, res, next) { try { const poamApprover = await poamApproverService.postPoamApprover(req, res, next); diff --git a/api/Controllers/PoamAssignees.js b/api/Controllers/PoamAssignees.js index 3aaaa5aa..1f97fefc 100644 --- a/api/Controllers/PoamAssignees.js +++ b/api/Controllers/PoamAssignees.js @@ -28,25 +28,6 @@ exports.getPoamAssigneesByPoamId = async function getPoamAssigneesByPoamId(req, } }; - -exports.getPoamAssigneesByUserId = async function getPoamAssigneesByUserId(req, res, next) { - try { - const result = await poamAssigneeService.getPoamAssigneesByUserId(req, res, next); - return res.status(200).json(result); - } catch (error) { - return res.status(500).json({ error: "An error occurred while retrieving POAM assignees by userId" }); - } -}; - -exports.getPoamAssignee = async function getPoamAssignee(req, res, next) { - try { - const result = await poamAssigneeService.getPoamAssignee(req, res, next); - return res.status(200).json(result); - } catch (error) { - return res.status(500).json({ error: "An error occurred while retrieving the POAM assignee" }); - } -}; - exports.postPoamAssignee = async function postPoamAssignee(req, res, next) { try { const assignee = await poamAssigneeService.postPoamAssignee(req, res, next); diff --git a/api/Controllers/PoamLabel.js b/api/Controllers/PoamLabel.js index e781735e..e5c46c93 100644 --- a/api/Controllers/PoamLabel.js +++ b/api/Controllers/PoamLabel.js @@ -26,7 +26,8 @@ module.exports.getPoamLabels = async function getPoamLabels(req, res, next) { module.exports.getAvailablePoamLabels = async function getAvailablePoamLabels(req, res, next) { try { - const poamLabels = await poamLabelService.getAvailablePoamLabels(req, res, next); + const userId = req.userObject.userId; + const poamLabels = await poamLabelService.getAvailablePoamLabels(userId); res.status(200).json(poamLabels); } catch (error) { if (error.message === 'User ID is required') { diff --git a/api/Controllers/User.js b/api/Controllers/User.js index a9657194..4ca69725 100644 --- a/api/Controllers/User.js +++ b/api/Controllers/User.js @@ -12,7 +12,9 @@ const userService = require('../Services/usersService'); module.exports.getUsers = async function getUsers(req, res, next) { try { - const users = await userService.getUsers(); + const userId = req.userObject.userId; + const elevate = req.query.elevate; + const users = await userService.getUsers(elevate, userId); res.status(200).json(users); } catch (error) { res.status(500).json({ error: 'Internal Server Error', detail: error.message }); @@ -21,8 +23,8 @@ module.exports.getUsers = async function getUsers(req, res, next) { module.exports.getCurrentUser = async function getCurrentUser(req, res, next) { try { - const email = req.userObject.email; - const user = await userService.getCurrentUser(email); + const userId = req.userObject.userId; + const user = await userService.getCurrentUser(userId); res.status(200).json(user); } catch (error) { if (error.message === "User not found") { @@ -36,36 +38,9 @@ module.exports.getCurrentUser = async function getCurrentUser(req, res, next) { module.exports.getUserByUserID = async function getUserByUserID(req, res, next) { try { const userId = req.params.userId; - const user = await userService.getUserByUserID(userId); - res.status(200).json(user); - } catch (error) { - if (error.message === "User not found") { - res.status(200).json(user); - } else { - res.status(500).json({ error: 'Internal Server Error', detail: error.message }); - } - } -}; - - -module.exports.getUserByUserName = async function getUserByUserName(req, res, next) { - try { - const userName = req.params.userName; - const user = await userService.getUserByUserName(userName); - res.status(200).json(user); - } catch (error) { - if (error.message === "User not found") { - res.status(200).json(user); - } else { - res.status(500).json({ error: 'Internal Server Error', detail: error.message }); - } - } -}; - -module.exports.getBasicUserByUserID = async function getBasicUserByUserID(req, res, next) { - try { - const userId = req.params.userId; - const user = await userService.getBasicUserByUserID(userId); + const requestorId = req.userObject.userId; + const elevate = req.query.elevate; + const user = await userService.getUserByUserID(requestorId, elevate, userId); res.status(200).json(user); } catch (error) { if (error.message === "User not found") { @@ -78,7 +53,9 @@ module.exports.getBasicUserByUserID = async function getBasicUserByUserID(req, r module.exports.updateUser = async function updateUser(req, res, next) { try { - const updatedUser = await userService.updateUser(req, res, next); + const userId = req.userObject.userId; + const elevate = req.query.elevate; + const updatedUser = await userService.updateUser(userId, elevate, req); res.status(200).json(updatedUser); } catch (error) { if (error.message === "User update failed") { @@ -104,7 +81,8 @@ module.exports.updateUserTheme = async function updateUserTheme(req, res, next) module.exports.updateUserPoints = async function updateUserPoints(req, res, next) { try { - const result = await userService.updateUserPoints(req, res, next); + const elevate = req.query.elevate; + const result = await userService.updateUserPoints(elevate, req); if (result.success) { res.status(200).json(result.message); } else { @@ -117,19 +95,12 @@ module.exports.updateUserPoints = async function updateUserPoints(req, res, next module.exports.deleteUser = async function deleteUser(req, res, next) { try { + const requestorId = req.userObject.userId; + const elevate = req.query.elevate; const userId = req.params.userId; - await userService.deleteUserByUserID(userId, req.userObject.username, req.userObject.displayName); + await userService.deleteUser(requestorId, elevate, userId); res.status(200).json({ message: 'User deleted' }); } catch (error) { res.status(500).json({ error: 'Internal Server Error', detail: error.message }); } -}; - -module.exports.loginState = async function loginState(req, res, next) { - try { - const message = await userService.loginState(req, res, next); - res.status(201).json(message); - } catch (error) { - res.status(500).json({ error: 'Internal Server Error', detail: error.message }); - } }; \ No newline at end of file diff --git a/api/Models/poam.model.js b/api/Models/poam.model.js index 1bfe84be..2fa4c1b0 100644 --- a/api/Models/poam.model.js +++ b/api/Models/poam.model.js @@ -67,14 +67,10 @@ module.exports = (sequelize, DataTypes) => { type: DataTypes.TEXT }, status: { - type: DataTypes.STRING(10), + type: DataTypes.STRING(50), allowNull: false, defaultValue: 'Draft' }, - vulnIdRestricted: { - type: DataTypes.STRING(255), - defaultValue: '' - }, submittedDate: { type: DataTypes.DATEONLY, defaultValue: '1900-01-01' @@ -116,11 +112,11 @@ module.exports = (sequelize, DataTypes) => { type: DataTypes.STRING(15), defaultValue: '' }, - businessImpactRating: { + impactRating: { type: DataTypes.STRING(15), defaultValue: '' }, - businessImpactDescription: { + impactDescription: { type: DataTypes.TEXT }, extensionTimeAllowed: { diff --git a/api/Services/collectionService.js b/api/Services/collectionService.js index 7f5c558e..5cb41957 100644 --- a/api/Services/collectionService.js +++ b/api/Services/collectionService.js @@ -13,6 +13,7 @@ const config = require('../utils/config') const dbUtils = require('./utils') const mysql = require('mysql2') const logger = require('../utils/logger') +const SmError = require('../utils/error') async function withConnection(callback) { const connection = await dbUtils.pool.getConnection(); @@ -23,77 +24,48 @@ async function withConnection(callback) { } } -exports.getCollections = async function getCollections(userNameInput, req, res, next) { - if (userNameInput == "Registrant") { - try { - return await withConnection(async (connection) => { - let sql = "SELECT collectionId, collectionName FROM cpat.collection;" - let [row] = await connection.query(sql) - const size = Object.keys(row).length - - const user = { - collections: [] - } - - for (let counter = 0; counter < size; counter++) { - user.collections.push({ - "collectionId": row[counter].collectionId, - "collectionName": row[counter].collectionName - }); - } - - return user; - }); - } catch (error) { - return { error: error.message }; - } - } - let userId = 0 - let isAdmin = 0; - let userName = ""; +exports.getCollections = async function getCollections(userId, elevate) { try { return await withConnection(async (connection) => { - let sql = "SELECT * FROM user WHERE userName = ?"; - let [row] = await connection.query(sql, [userNameInput]); - userId = row[0].userId; - isAdmin = row[0].isAdmin; - - const user = { - collections: [] - } - if (isAdmin == 1) { - let sql2 = "SELECT * FROM collection;" - let [row2] = await connection.query(sql2) - const size = Object.keys(row2).length - - for (let counter = 0; counter < size; counter++) { - user.collections.push({ - ...row2[counter] - }); + if (elevate) { + let sql = "SELECT * FROM user WHERE userId = ?"; + let [rows] = await connection.query(sql, [userId]); + if (rows.length === 0) { + throw new SmError.PrivilegeError('User not found'); } - - return user.collections; - } else { - let sql = "SELECT * FROM collectionpermissions WHERE userId = ?"; - let [row2] = await connection.query(sql, [userId]) - const numberOfCollections = Object.keys(row2).length - const nonAdminCollections = { + let isAdmin = rows[0].isAdmin; + const user = { collections: [] } - for (let counter = 0; counter < numberOfCollections; counter++) { - let sql3 = "SELECT * FROM collection WHERE collectionId = ?"; - let [row3] = await connection.query(sql3, [row2[counter].collectionId]) - nonAdminCollections.collections.push({ - "collectionId": row3[0].collectionId, - "collectionName": row3[0].collectionName, - "description": row3[0].description, - "created": row3[0].created, - "assetCount": row3[0].assetCount, - "poamCount": row3[0].poamCount - }); + if (isAdmin == 1) { + let sql2 = "SELECT * FROM collection;" + let [row2] = await connection.query(sql2) + const size = Object.keys(row2).length + for (let counter = 0; counter < size; counter++) { + user.collections.push({ + ...row2[counter] + }); + } + return user.collections; + } else { + throw new SmError.PrivilegeError('User requesting Elevate without admin permissions.'); } - - return nonAdminCollections.collections; + } else { + let collectionSql = ` + SELECT c.* + FROM collection c + INNER JOIN collectionpermissions cp ON c.collectionId = cp.collectionId + WHERE cp.userId = ?; + `; + let [collections] = await connection.query(collectionSql, [userId]); + return collections.map(collection => ({ + collectionId: collection.collectionId, + collectionName: collection.collectionName, + description: collection.description, + created: collection.created, + assetCount: collection.assetCount, + poamCount: collection.poamCount + })); } }); } diff --git a/api/Services/importService.js b/api/Services/importService.js index f932b36d..1d852700 100644 --- a/api/Services/importService.js +++ b/api/Services/importService.js @@ -50,8 +50,8 @@ const excelColumnToDbColumnMapping = { "Relevance of Threat": "relevanceOfThreat", "Threat Description": "threatDescription", "Likelihood": "likelihood", - "Impact": "businessImpactRating", - "Impact Description": "businessImpactDescription", + "Impact": "impactRating", + "Impact Description": "impactDescription", "Residual Risk Level": "residualRisk", "Recommendations": "recommendations", "Resulting Residual Risk after Proposed Mitigations": "adjSeverity" @@ -60,13 +60,15 @@ const excelColumnToDbColumnMapping = { function mapValueToCategory(cellValue, dbColumn) { const severityMapping = { rawSeverity: { - 'I': "Cat I - Critical/High", - 'II': "CAT II - Medium", - 'III': "CAT III - Low" + 'Very High': "CAT I - Critical", + 'High': "CAT I - High", + 'Moderate': "CAT II - Medium", + 'Low': "CAT III - Low", + 'Very Low': "CAT III - Low" }, adjSeverity: { - 'Very High': "Cat I - Critical/High", - 'High': "Cat I - Critical/High", + 'Very High': "CAT I - Critical", + 'High': "CAT I - High", 'Moderate': "CAT II - Medium", 'Low': "CAT III - Low", 'Very Low': "CAT III - Low" @@ -277,25 +279,6 @@ function processVulnerabilitySource(poamEntry, cellValue) { } } -function mapValueToCategory(cellValue, dbColumn) { - const severityMapping = { - rawSeverity: { - 'I': "Cat I - Critical/High", - 'II': "CAT II - Medium", - 'III': "CAT III - Low" - }, - adjSeverity: { - 'Very High': "Cat I - Critical/High", - 'High': "Cat I - Critical/High", - 'Moderate': "CAT II - Medium", - 'Low': "CAT III - Low", - 'Very Low': "CAT III - Low" - } - }; - - return severityMapping[dbColumn][cellValue] || cellValue; -} - async function processPoamEntry(poamEntry, collectionId) { let poam = await findOrCreatePoam(poamEntry); await updatePoamMilestones(poam.poamId, poamEntry.milestones, poamEntry.milestoneChanges); diff --git a/api/Services/marketplaceService.js b/api/Services/marketplaceService.js index feac9b1e..14a534d7 100644 --- a/api/Services/marketplaceService.js +++ b/api/Services/marketplaceService.js @@ -22,6 +22,8 @@ async function withConnection(callback) { } } + + exports.getAllThemes = async function getAllThemes() { try { return await withConnection(async (connection) => { diff --git a/api/Services/metricsService.js b/api/Services/metricsService.js index 41566872..15a36b78 100644 --- a/api/Services/metricsService.js +++ b/api/Services/metricsService.js @@ -23,7 +23,9 @@ async function withConnection(callback) { } } -exports.getCollectionAssetLabel = async function getCollectionAssetLabel(req, res, next) { + + +exports.getCollectionAssetLabel = async function getCollectionAssetLabel(collectionId) { try { return await withConnection(async (connection) => { let sql = ` @@ -34,7 +36,7 @@ exports.getCollectionAssetLabel = async function getCollectionAssetLabel(req, re WHERE p.collectionId = ? GROUP BY l.labelName; `; - let [rows] = await connection.query(sql, [req.params.collectionId]); + let [rows] = await connection.query(sql, [collectionId]); let assetLabel = rows.map(row => ({ label: row.labelName, @@ -48,11 +50,11 @@ exports.getCollectionAssetLabel = async function getCollectionAssetLabel(req, re } } -exports.getCollectionPoamStatus = async function getCollectionPoamStatus(req, res, next) { +exports.getCollectionPoamStatus = async function getCollectionPoamStatus(collectionId) { try { return await withConnection(async (connection) => { let sql = "SELECT status, COUNT(*) AS statusCount FROM poam WHERE collectionId = ? GROUP BY status;" - let [rows] = await connection.query(sql, [req.params.collectionId]) + let [rows] = await connection.query(sql, [collectionId]) const size = Object.keys(rows).length @@ -72,7 +74,7 @@ exports.getCollectionPoamStatus = async function getCollectionPoamStatus(req, re } } -exports.getCollectionPoamLabel = async function getCollectionPoamLabel(req, res, next) { +exports.getCollectionPoamLabel = async function getCollectionPoamLabel(collectionId) { try { return await withConnection(async (connection) => { let sql = ` @@ -83,7 +85,7 @@ exports.getCollectionPoamLabel = async function getCollectionPoamLabel(req, res, WHERE p.collectionId = ? GROUP BY l.labelName; `; - let [rows] = await connection.query(sql, [req.params.collectionId]); + let [rows] = await connection.query(sql, [collectionId]); let poamLabel = rows.map(row => ({ label: row.labelName, @@ -97,11 +99,11 @@ exports.getCollectionPoamLabel = async function getCollectionPoamLabel(req, res, } } -exports.getCollectionPoamSeverity = async function getCollectionPoamSeverity(req, res, next) { +exports.getCollectionPoamSeverity = async function getCollectionPoamSeverity(collectionId) { try { return await withConnection(async (connection) => { let sql = "SELECT rawSeverity, COUNT(*) AS severityCount FROM poam WHERE collectionId = ? GROUP BY rawSeverity;" - let [rows] = await connection.query(sql, [req.params.collectionId]) + let [rows] = await connection.query(sql, [collectionId]) const size = Object.keys(rows).length @@ -122,7 +124,7 @@ exports.getCollectionPoamSeverity = async function getCollectionPoamSeverity(req } } -exports.getCollectionMonthlyPoamStatus = async function getCollectionMonthlyPoamStatus(req, res, next) { +exports.getCollectionMonthlyPoamStatus = async function getCollectionMonthlyPoamStatus(collectionId) { try { return await withConnection(async (connection) => { let sql = ` @@ -139,7 +141,7 @@ exports.getCollectionMonthlyPoamStatus = async function getCollectionMonthlyPoam GROUP BY status; `; - let [rows] = await connection.query(sql, [req.params.collectionId]); + let [rows] = await connection.query(sql, [collectionId]); const poamStatusMap = {}; @@ -167,7 +169,7 @@ exports.getCollectionMonthlyPoamStatus = async function getCollectionMonthlyPoam } }; -exports.getCollectionPoamScheduledCompletion = async function getCollectionPoamScheduledCompletion(req, res, next) { +exports.getCollectionPoamScheduledCompletion = async function getCollectionPoamScheduledCompletion(collectionId) { try { return await withConnection(async (connection) => { let sql = ` @@ -176,7 +178,7 @@ exports.getCollectionPoamScheduledCompletion = async function getCollectionPoamS WHERE collectionId = ? `; - let [rows] = await connection.query(sql, [req.params.collectionId]); + let [rows] = await connection.query(sql, [collectionId]); let buckets = { "OVERDUE": 0, @@ -211,11 +213,9 @@ exports.getCollectionPoamScheduledCompletion = async function getCollectionPoamS } } -exports.getAvailableAssetLabel = async function getAvailableAssetLabel(req, res, next) { +exports.getAvailableAssetLabel = async function getAvailableAssetLabel(userId) { try { return await withConnection(async (connection) => { - const userId = req.params.userId; - const [adminRows] = await connection.query("SELECT isAdmin FROM cpat.user WHERE userId = ?", [userId]); const isAdmin = adminRows[0].isAdmin; @@ -261,11 +261,9 @@ exports.getAvailableAssetLabel = async function getAvailableAssetLabel(req, res, } } -exports.getAvailablePoamStatus = async function getAvailablePoamStatus(req, res, next) { +exports.getAvailablePoamStatus = async function getAvailablePoamStatus(userId) { try { return await withConnection(async (connection) => { - const userId = req.params.userId; - const [adminRows] = await connection.query("SELECT isAdmin FROM cpat.user WHERE userId = ?", [userId]); const isAdmin = adminRows[0].isAdmin; @@ -306,11 +304,9 @@ exports.getAvailablePoamStatus = async function getAvailablePoamStatus(req, res, } } -exports.getAvailableMonthlyPoamStatus = async function getAvailableMonthlyPoamStatus(req, res, next) { +exports.getAvailableMonthlyPoamStatus = async function getAvailableMonthlyPoamStatus(userId) { try { return await withConnection(async (connection) => { - const userId = req.params.userId; - const [adminRows] = await connection.query("SELECT isAdmin FROM cpat.user WHERE userId = ?", [userId]); const isAdmin = adminRows[0].isAdmin; @@ -375,11 +371,9 @@ exports.getAvailableMonthlyPoamStatus = async function getAvailableMonthlyPoamSt } }; -exports.getAvailablePoamLabel = async function getAvailablePoamLabel(req, res, next) { +exports.getAvailablePoamLabel = async function getAvailablePoamLabel(userId) { try { return await withConnection(async (connection) => { - const userId = req.params.userId; - const [adminRows] = await connection.query("SELECT isAdmin FROM cpat.user WHERE userId = ?", [userId]); const isAdmin = adminRows[0].isAdmin; @@ -425,11 +419,9 @@ exports.getAvailablePoamLabel = async function getAvailablePoamLabel(req, res, n } } -exports.getAvailableCollectionPoamCounts = async function getAvailableCollectionPoamCounts(req, res, next) { +exports.getAvailableCollectionPoamCounts = async function getAvailableCollectionPoamCounts(userId) { try { return await withConnection(async (connection) => { - const userId = req.params.userId; - const [adminRows] = await connection.query("SELECT isAdmin FROM cpat.user WHERE userId = ?", [userId]); const isAdmin = adminRows[0].isAdmin; @@ -464,11 +456,9 @@ exports.getAvailableCollectionPoamCounts = async function getAvailableCollection } }; -exports.getAvailablePoamSeverity = async function getAvailablePoamSeverity(req, res, next) { +exports.getAvailablePoamSeverity = async function getAvailablePoamSeverity(userId) { try { return await withConnection(async (connection) => { - const userId = req.params.userId; - const [adminRows] = await connection.query("SELECT isAdmin FROM cpat.user WHERE userId = ?", [userId]); const isAdmin = adminRows[0].isAdmin; @@ -509,11 +499,9 @@ exports.getAvailablePoamSeverity = async function getAvailablePoamSeverity(req, } } -exports.getAvailableMonthlyPoamSeverity = async function getAvailableMonthlyPoamSeverity(req, res, next) { +exports.getAvailableMonthlyPoamSeverity = async function getAvailableMonthlyPoamSeverity(userId) { try { return await withConnection(async (connection) => { - const userId = req.params.userId; - const [adminRows] = await connection.query("SELECT isAdmin FROM cpat.user WHERE userId = ?", [userId]); const isAdmin = adminRows[0].isAdmin; @@ -553,11 +541,9 @@ exports.getAvailableMonthlyPoamSeverity = async function getAvailableMonthlyPoam } }; -exports.getAvailablePoamScheduledCompletion = async function getAvailablePoamScheduledCompletion(req, res, next) { +exports.getAvailablePoamScheduledCompletion = async function getAvailablePoamScheduledCompletion(userId) { try { return await withConnection(async (connection) => { - const userId = req.params.userId; - const [adminRows] = await connection.query("SELECT isAdmin FROM cpat.user WHERE userId = ?", [userId]); const isAdmin = adminRows[0].isAdmin; diff --git a/api/Services/migrations/sql/current/10-cpat-tables.sql b/api/Services/migrations/sql/current/10-cpat-tables.sql index 3752db58..982c53d5 100644 --- a/api/Services/migrations/sql/current/10-cpat-tables.sql +++ b/api/Services/migrations/sql/current/10-cpat-tables.sql @@ -204,7 +204,7 @@ DROP TABLE IF EXISTS `poam`; CREATE TABLE `poam` ( `poamId` int NOT NULL AUTO_INCREMENT, `collectionId` int DEFAULT '0', - `status` char(20) DEFAULT 'Draft', + `status` char(50) DEFAULT 'Draft', `rawSeverity` varchar(25) DEFAULT '', `adjSeverity` varchar(25) DEFAULT '', `vulnerabilitySource` varchar(255) DEFAULT '', @@ -223,7 +223,6 @@ CREATE TABLE `poam` ( `mitigations` text, `requiredResources` text, `residualRisk` varchar(25) DEFAULT NULL, - `vulnIdRestricted` varchar(255) DEFAULT '', `securityControlNumber` varchar(25) DEFAULT '', `submitterId` int NOT NULL DEFAULT '0', `officeOrg` varchar(100) DEFAULT '', @@ -232,8 +231,8 @@ CREATE TABLE `poam` ( `relevanceOfThreat` varchar(15) DEFAULT '', `threatDescription` varchar(255) DEFAULT '', `likelihood` varchar(15) DEFAULT '', - `businessImpactRating` varchar(15) DEFAULT '', - `businessImpactDescription` varchar(2000) DEFAULT '', + `impactRating` varchar(15) DEFAULT '', + `impactDescription` varchar(2000) DEFAULT '', `extensionTimeAllowed` int DEFAULT '0', `extensionJustification` varchar(2000) DEFAULT '', `emassStatus` varchar(15) DEFAULT 'Ongoing', @@ -253,7 +252,7 @@ CREATE TABLE `poam` ( /*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; DELIMITER ;; -/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `after_poam_insert` AFTER INSERT ON `poam` FOR EACH ROW BEGIN +/*!50003 CREATE*/ /*!50003 TRIGGER `after_poam_insert` AFTER INSERT ON `poam` FOR EACH ROW BEGIN UPDATE `collection` SET `poamCount` = `poamCount` + 1 WHERE `collectionId` = NEW.`collectionId`; @@ -266,7 +265,7 @@ DELIMITER ; /*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; DELIMITER ;; -/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `prevent_created_update` BEFORE UPDATE ON `poam` FOR EACH ROW BEGIN +/*!50003 CREATE*/ /*!50003 TRIGGER `prevent_created_update` BEFORE UPDATE ON `poam` FOR EACH ROW BEGIN IF NEW.created != OLD.created THEN SET NEW.created = OLD.created; END IF; @@ -279,7 +278,7 @@ DELIMITER ; /*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; DELIMITER ;; -/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `after_poam_update` AFTER UPDATE ON `poam` FOR EACH ROW BEGIN +/*!50003 CREATE*/ /*!50003 TRIGGER `after_poam_update` AFTER UPDATE ON `poam` FOR EACH ROW BEGIN IF OLD.`collectionId` != NEW.`collectionId` THEN UPDATE `collection` c SET c.`poamCount` = c.`poamCount` - 1 @@ -298,22 +297,7 @@ DELIMITER ; /*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; DELIMITER ;; -/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `after_poam_update_last_updated` AFTER UPDATE ON `poam` FOR EACH ROW BEGIN - IF OLD.lastUpdated != NEW.lastUpdated THEN - UPDATE poam - SET lastUpdated = CURRENT_DATE - WHERE poamId = NEW.poamId; - END IF; -END */;; -DELIMITER ; -/*!50003 SET sql_mode = @saved_sql_mode */ ; -/*!50003 SET collation_connection = @saved_col_connection */ ; -/*!50003 SET @saved_col_connection = @@collation_connection */ ; -/*!50003 SET collation_connection = utf8mb4_0900_ai_ci */ ; -/*!50003 SET @saved_sql_mode = @@sql_mode */ ; -/*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; -DELIMITER ;; -/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `after_poam_delete` AFTER DELETE ON `poam` FOR EACH ROW BEGIN +/*!50003 CREATE*/ /*!50003 TRIGGER `after_poam_delete` AFTER DELETE ON `poam` FOR EACH ROW BEGIN UPDATE `collection` SET `poamCount` = `poamCount` - 1 WHERE `collectionId` = OLD.`collectionId`; @@ -340,7 +324,7 @@ CREATE TABLE `poamapprovers` ( /*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; DELIMITER ;; -/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `after_poamapprovers_insert` AFTER INSERT ON `poamapprovers` FOR EACH ROW BEGIN +/*!50003 CREATE*/ /*!50003 TRIGGER `after_poamapprovers_insert` AFTER INSERT ON `poamapprovers` FOR EACH ROW BEGIN UPDATE poam SET lastUpdated = CURRENT_DATE WHERE poamId = NEW.poamId; @@ -353,7 +337,7 @@ DELIMITER ; /*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; DELIMITER ;; -/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `after_poamapprovers_update` AFTER UPDATE ON `poamapprovers` FOR EACH ROW BEGIN +/*!50003 CREATE*/ /*!50003 TRIGGER `after_poamapprovers_update` AFTER UPDATE ON `poamapprovers` FOR EACH ROW BEGIN UPDATE poam SET lastUpdated = CURRENT_DATE WHERE poamId = NEW.poamId; @@ -366,7 +350,7 @@ DELIMITER ; /*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; DELIMITER ;; -/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `after_poamapprovers_delete` AFTER DELETE ON `poamapprovers` FOR EACH ROW BEGIN +/*!50003 CREATE*/ /*!50003 TRIGGER `after_poamapprovers_delete` AFTER DELETE ON `poamapprovers` FOR EACH ROW BEGIN UPDATE poam SET lastUpdated = CURRENT_DATE WHERE poamId = OLD.poamId; @@ -393,7 +377,7 @@ CREATE TABLE `poamassets` ( /*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; DELIMITER ;; -/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `after_poamassets_insert` AFTER INSERT ON `poamassets` FOR EACH ROW BEGIN +/*!50003 CREATE*/ /*!50003 TRIGGER `after_poamassets_insert` AFTER INSERT ON `poamassets` FOR EACH ROW BEGIN UPDATE poam SET lastUpdated = CURRENT_DATE WHERE poamId = NEW.poamId; @@ -406,7 +390,7 @@ DELIMITER ; /*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; DELIMITER ;; -/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `after_poamassets_delete` AFTER DELETE ON `poamassets` FOR EACH ROW BEGIN +/*!50003 CREATE*/ /*!50003 TRIGGER `after_poamassets_delete` AFTER DELETE ON `poamassets` FOR EACH ROW BEGIN UPDATE poam SET lastUpdated = CURRENT_DATE WHERE poamId = OLD.poamId; @@ -431,7 +415,7 @@ CREATE TABLE `poamassignees` ( /*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; DELIMITER ;; -/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `after_poamassignees_insert` AFTER INSERT ON `poamassignees` FOR EACH ROW BEGIN +/*!50003 CREATE*/ /*!50003 TRIGGER `after_poamassignees_insert` AFTER INSERT ON `poamassignees` FOR EACH ROW BEGIN UPDATE poam SET lastUpdated = CURRENT_DATE WHERE poamId = NEW.poamId; @@ -444,7 +428,7 @@ DELIMITER ; /*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; DELIMITER ;; -/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `after_poamassignees_update` AFTER UPDATE ON `poamassignees` FOR EACH ROW BEGIN +/*!50003 CREATE*/ /*!50003 TRIGGER `after_poamassignees_update` AFTER UPDATE ON `poamassignees` FOR EACH ROW BEGIN UPDATE poam SET lastUpdated = CURRENT_DATE WHERE poamId = NEW.poamId; @@ -457,7 +441,7 @@ DELIMITER ; /*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; DELIMITER ;; -/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `after_poamassignees_delete` AFTER DELETE ON `poamassignees` FOR EACH ROW BEGIN +/*!50003 CREATE*/ /*!50003 TRIGGER `after_poamassignees_delete` AFTER DELETE ON `poamassignees` FOR EACH ROW BEGIN UPDATE poam SET lastUpdated = CURRENT_DATE WHERE poamId = OLD.poamId; @@ -481,7 +465,7 @@ CREATE TABLE `poamlabels` ( /*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; DELIMITER ;; -/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `after_poamlabels_insert` AFTER INSERT ON `poamlabels` FOR EACH ROW BEGIN +/*!50003 CREATE*/ /*!50003 TRIGGER `after_poamlabels_insert` AFTER INSERT ON `poamlabels` FOR EACH ROW BEGIN UPDATE poam SET lastUpdated = CURRENT_DATE WHERE poamId = NEW.poamId; @@ -494,7 +478,7 @@ DELIMITER ; /*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; DELIMITER ;; -/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `after_poamlabels_update` AFTER UPDATE ON `poamlabels` FOR EACH ROW BEGIN +/*!50003 CREATE*/ /*!50003 TRIGGER `after_poamlabels_update` AFTER UPDATE ON `poamlabels` FOR EACH ROW BEGIN UPDATE poam SET lastUpdated = CURRENT_DATE WHERE poamId = NEW.poamId; @@ -507,7 +491,7 @@ DELIMITER ; /*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; DELIMITER ;; -/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `after_poamlabels_delete` AFTER DELETE ON `poamlabels` FOR EACH ROW BEGIN +/*!50003 CREATE*/ /*!50003 TRIGGER `after_poamlabels_delete` AFTER DELETE ON `poamlabels` FOR EACH ROW BEGIN UPDATE poam SET lastUpdated = CURRENT_DATE WHERE poamId = OLD.poamId; @@ -535,7 +519,7 @@ CREATE TABLE `poamlogs` ( /*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; DELIMITER ;; -/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `after_poamlogs_insert` AFTER INSERT ON `poamlogs` FOR EACH ROW BEGIN +/*!50003 CREATE*/ /*!50003 TRIGGER `after_poamlogs_insert` AFTER INSERT ON `poamlogs` FOR EACH ROW BEGIN UPDATE poam SET lastUpdated = CURRENT_DATE WHERE poamId = NEW.poamId; @@ -566,7 +550,7 @@ CREATE TABLE `poammilestones` ( /*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; DELIMITER ;; -/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `after_poammilestones_insert` AFTER INSERT ON `poammilestones` FOR EACH ROW BEGIN +/*!50003 CREATE*/ /*!50003 TRIGGER `after_poammilestones_insert` AFTER INSERT ON `poammilestones` FOR EACH ROW BEGIN UPDATE poam SET lastUpdated = CURRENT_DATE WHERE poamId = NEW.poamId; @@ -579,7 +563,7 @@ DELIMITER ; /*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; DELIMITER ;; -/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `after_poammilestones_update` AFTER UPDATE ON `poammilestones` FOR EACH ROW BEGIN +/*!50003 CREATE*/ /*!50003 TRIGGER `after_poammilestones_update` AFTER UPDATE ON `poammilestones` FOR EACH ROW BEGIN UPDATE poam SET lastUpdated = CURRENT_DATE WHERE poamId = NEW.poamId; @@ -592,7 +576,7 @@ DELIMITER ; /*!50003 SET @saved_sql_mode = @@sql_mode */ ; /*!50003 SET sql_mode = 'ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION' */ ; DELIMITER ;; -/*!50003 CREATE*/ /*!50017 DEFINER=`root`@`localhost`*/ /*!50003 TRIGGER `after_poammilestones_delete` AFTER DELETE ON `poammilestones` FOR EACH ROW BEGIN +/*!50003 CREATE*/ /*!50003 TRIGGER `after_poammilestones_delete` AFTER DELETE ON `poammilestones` FOR EACH ROW BEGIN UPDATE poam SET lastUpdated = CURRENT_DATE WHERE poamId = OLD.poamId; @@ -623,16 +607,16 @@ DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `userId` int NOT NULL AUTO_INCREMENT, `userName` varchar(20) NOT NULL, - `email` varchar(100) NOT NULL, - `firstName` varchar(50) NOT NULL, - `lastName` varchar(50) NOT NULL, + `email` varchar(100) NOT NULL DEFAULT ' ', + `firstName` varchar(50) NOT NULL DEFAULT ' ', + `lastName` varchar(50) NOT NULL DEFAULT ' ', `created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `lastAccess` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `lastCollectionAccessedId` int NOT NULL DEFAULT '0', `accountStatus` varchar(25) NOT NULL DEFAULT 'PENDING', `fullName` varchar(100) DEFAULT NULL, `officeOrg` varchar(100) DEFAULT 'UNKNOWN', - `defaultTheme` varchar(50) DEFAULT 'dark', + `defaultTheme` varchar(50) DEFAULT 'lara-dark-blue', `isAdmin` int NOT NULL DEFAULT '0', `lastClaims` json DEFAULT (_utf8mb4'{}'), `points` int NOT NULL DEFAULT '0', @@ -650,4 +634,4 @@ CREATE TABLE `user` ( /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2024-08-19 12:21:32 +-- Dump completed on 2024-08-23 14:22:45 diff --git a/api/Services/migrations/sql/current/20-cpat-static.sql b/api/Services/migrations/sql/current/20-cpat-static.sql index 20f9bfa8..c9333186 100644 --- a/api/Services/migrations/sql/current/20-cpat-static.sql +++ b/api/Services/migrations/sql/current/20-cpat-static.sql @@ -27,7 +27,7 @@ UNLOCK TABLES; LOCK TABLES `themes` WRITE; /*!40000 ALTER TABLE `themes` DISABLE KEYS */; -INSERT INTO `themes` VALUES (1,'bootstrap4-dark-purple','Bootstrap Dark Purple','Welcome to the Eggplant Colored Extravaganza! This theme is so aubergine, you\'ll be dreaming of ratatouille while managing your POAMs.',150),(2,'bootstrap4-light-purple','Bootstrap Light Purple','Feeling royal? This lavender luxury will have you sipping imaginary Earl Grey tea while conquering your POAMs like a proper monarch.',50),(3,'bootstrap4-dark-blue','Bootstrap Dark Blue','Dive into the Deep Blue Data Sea! This theme is so oceanic, you might need scuba gear to navigate your tasks. Watch out for the occasional digital jellyfish - they sting with extra work!',150),(4,'bootstrap4-light-blue','Bootstrap Light Blue','Welcome to Cloud Nine Computing! This sky-blue theme is so light and airy, your POAMs might just float away. Don\'t forget to tie them down with a virtual paperweight!',50),(5,'soho-dark','Soho Dark','Step into the Soho Speakeasy of Productivity! This sleek, dark theme is so hip, your POAMs will start wearing tiny berets and discussing existential philosophy.',200),(6,'soho-light','Soho Light','Ah, Soho-light - where your POAMs dress in designer labels and sip overpriced lattes. It\'s so trendy, your tasks might start referring to themselves as \'bespoke action items.\' Just remember, even if a POAM asks for avocado toast, it\'s still work!',50),(7,'viva-dark','Viva Dark','Step into the Viva Las Vegas of Productivity! This dark, sleek theme is so alluring, your POAMs might start doing Elvis impersonations.',100),(8,'viva-light','Viva Light','Viva-light, where your POAMs party like it\'s 1999, but it\'s actually Monday morning. This theme is so bright and cheerful, your tasks might start wearing sunglasses indoors and calling everyone \'dude.\' ',50); +INSERT INTO `themes` VALUES (1,'bootstrap4-dark-purple','Bootstrap Dark Purple','Welcome to the Eggplant Colored Extravaganza! This theme is so aubergine, you\'ll be dreaming of ratatouille while managing your POAMs.',150),(2,'bootstrap4-light-purple','Bootstrap Light Purple','Feeling royal? This lavender luxury will have you sipping imaginary Earl Grey tea while conquering your POAMs like a proper monarch.',50),(3,'bootstrap4-dark-blue','Bootstrap Dark Blue','Dive into the Deep Blue Data Sea! This theme is so oceanic, you might need scuba gear to navigate your tasks. Watch out for the occasional digital jellyfish - they sting with extra work!',150),(4,'bootstrap4-light-blue','Bootstrap Light Blue','Welcome to Cloud Nine Computing! This sky-blue theme is so light and airy, your POAMs might just float away. Don\'t forget to tie them down with a virtual paperweight!',50),(5,'soho-dark','Soho Dark','Step into the Soho Speakeasy of Productivity! This sleek, dark theme is so hip, your POAMs will start wearing tiny berets and discussing existential philosophy.',200),(6,'soho-light','Soho Light','Ah, Soho-light - where your POAMs dress in designer labels and sip overpriced lattes. It\'s so trendy, your tasks might start referring to themselves as \'bespoke action items.\' Just remember, even if a POAM asks for avocado toast, it\'s still work!',50),(7,'viva-dark','Viva Dark','Step into the Viva Las Vegas of Productivity! This dark, sleek theme is so alluring, your POAMs might start doing Elvis impersonations.',100),(8,'viva-light','Viva Light','Viva-light, where your POAMs party like it\'s 1999, but it\'s actually Monday morning. This theme is so bright and cheerful, your tasks might start wearing sunglasses indoors and calling everyone dude.',50); /*!40000 ALTER TABLE `themes` ENABLE KEYS */; UNLOCK TABLES; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; @@ -38,4 +38,4 @@ UNLOCK TABLES; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2024-08-19 12:21:32 +-- Dump completed on 2024-08-23 14:22:45 diff --git a/api/Services/notificationService.js b/api/Services/notificationService.js index 2a1e340c..4c673169 100644 --- a/api/Services/notificationService.js +++ b/api/Services/notificationService.js @@ -21,7 +21,10 @@ async function withConnection(callback) { await connection.release(); } } -exports.getAllNotificationsByUserId = async function getAllNotificationsByUserId(userId) { + + + +exports.getAllNotifications = async function getAllNotifications(userId) { try { return await withConnection(async (connection) => { const sql = `SELECT * FROM cpat.notification WHERE userId = ? ORDER BY timestamp DESC`; @@ -33,7 +36,7 @@ exports.getAllNotificationsByUserId = async function getAllNotificationsByUserId } }; -exports.getUnreadNotificationsByUserId = async function getUnreadNotificationsByUserId(userId) { +exports.getUnreadNotifications = async function getUnreadNotifications(userId) { try { return await withConnection(async (connection) => { const sql = `SELECT * FROM cpat.notification WHERE notification.read = 0 AND userId = ? ORDER BY timestamp DESC`; @@ -45,7 +48,7 @@ exports.getUnreadNotificationsByUserId = async function getUnreadNotificationsBy } }; -exports.getUnreadNotificationCountByUserId = async function getUnreadNotificationCountByUserId(userId) { +exports.getUnreadNotificationCount = async function getUnreadNotificationCount(userId) { try { return await withConnection(async (connection) => { const sql = `SELECT COUNT(userId) AS NotificationCount FROM cpat.notification WHERE notification.read = 0 AND userId = ?`; @@ -58,7 +61,7 @@ exports.getUnreadNotificationCountByUserId = async function getUnreadNotificatio } }; -exports.dismissNotificationByNotificationId = async function dismissNotificationByNotificationId(notificationId, userId) { +exports.dismissNotification = async function dismissNotification(userId, notificationId) { try { return await withConnection(async (connection) => { const userIdQuery = `SELECT userId FROM cpat.notification WHERE notificationId = ?`; @@ -77,7 +80,7 @@ exports.dismissNotificationByNotificationId = async function dismissNotification } }; -exports.dismissAllNotificationsByUserId = async function dismissAllNotificationsByUserId(userId) { +exports.dismissAllNotifications = async function dismissAllNotifications(userId) { try { return await withConnection(async (connection) => { const sql = `UPDATE cpat.notification SET notification.read = 1 WHERE userId = ?`; @@ -89,11 +92,11 @@ exports.dismissAllNotificationsByUserId = async function dismissAllNotifications } }; -exports.deleteNotificationByNotificationId = async function deleteNotificationByNotificationId(notificationId) { +exports.deleteNotification = async function deleteNotification(userId, notificationId) { try { return await withConnection(async (connection) => { - const sql = `DELETE FROM cpat.notification WHERE notificationId = ?`; - const [result] = await connection.query(sql, [notificationId]); + const sql = `DELETE FROM cpat.notification WHERE notificationId = ? AND userId = ?`; + const [result] = await connection.query(sql, [notificationId, userId]); return result.affectedRows > 0; }); } catch (error) { @@ -101,7 +104,7 @@ exports.deleteNotificationByNotificationId = async function deleteNotificationBy } }; -exports.deleteAllNotificationsByUserId = async function deleteAllNotificationsByUserId(userId) { +exports.deleteAllNotifications = async function deleteAllNotifications(userId) { try { return await withConnection(async (connection) => { const sql = `DELETE FROM cpat.notification WHERE userId = ?`; diff --git a/api/Services/permissionsService.js b/api/Services/permissionsService.js index 033e6ce7..318739e4 100644 --- a/api/Services/permissionsService.js +++ b/api/Services/permissionsService.js @@ -22,51 +22,6 @@ async function withConnection(callback) { } } -exports.getCollectionPermission = async function getCollectionPermission(userName, collectionId, req, res, next) { - function collectionObj(collectionId, collectionName, description, created, poamCount) { - this.collectionId = collectionId - this.collectionName = collectionName - this.description = description - this.created = created - this.poamCount = poamCount - } - - try { - let userInfo = await withConnection(async (connection) => { - let sql = "SELECT * FROM user WHERE userName = ?"; - let [row] = await connection.query(sql, [userName]); - let userId = row[0].userId; - let isAdmin = row[0].isAdmin; - - try { - let sql = "SELECT * FROM collectionpermissions WHERE userId = ? AND collectionId = ?"; - let [row] = await connection.query(sql, [userId, collectionId]); - let userName = row[0].userName; - return { userId, isAdmin, userName }; - } catch (error) { - if (!isAdmin) { - return { error: "User does not have access to this collection." }; - } - return { userId, isAdmin }; - } - }); - - if (userInfo.error) { - return { error: userInfo.error }; - } - - let collection = await withConnection(async (connection) => { - let sql = "SELECT * FROM collection WHERE collectionId = ?"; - let [row] = await connection.query(sql, [collectionId]); - return { ...row[0] }; - }); - - return collection; - } catch (error) { - return { error: error.message }; - } -} - exports.getCollectionPermissions = async function getCollectionPermissions(req, res, next) { if (!req.params.collectionId) { return next({ @@ -90,11 +45,13 @@ exports.getCollectionPermissions = async function getCollectionPermissions(req, return permissions; } catch (error) { - return { error: error.message }; + return { + error: error.message + }; } } -exports.postPermission = async function postPermission(req, res, next) { +exports.postPermission = async function postPermission(userId, elevate, req) { if (!req.body.userId) { return next({ status: 400, @@ -124,15 +81,33 @@ exports.postPermission = async function postPermission(req, res, next) { try { return await withConnection(async (connection) => { - let sql_query = "INSERT INTO cpat.collectionpermissions (accessLevel, userId, collectionId) VALUES (?, ?, ?);"; - await connection.query(sql_query, [req.body.accessLevel, req.body.userId, req.body.collectionId]); + if (elevate) { + const userSql = "SELECT * FROM user WHERE userId = ?"; + const [userRows] = await connection.query(userSql, [userId]); - const message = { - userId: req.body.userId, - collectionId: req.body.collectionId, - accessLevel: req.body.accessLevel, - }; - return message; + if (userRows.length === 0) { + throw new SmError.PrivilegeError('User not found'); + } + + const isAdmin = userRows[0].isAdmin; + + if (isAdmin !== 1) { + throw new SmError.PrivilegeError('User requesting Elevate without admin permissions.'); + } + + let sql_query = "INSERT INTO cpat.collectionpermissions (accessLevel, userId, collectionId) VALUES (?, ?, ?);"; + await connection.query(sql_query, [req.body.accessLevel, req.body.userId, req.body.collectionId]); + + const message = { + userId: req.body.userId, + collectionId: req.body.collectionId, + accessLevel: req.body.accessLevel, + }; + return message; + + } else { + throw new Error('Elevate parameter is required'); + } }); } catch (error) { if (error.code === 'ER_DUP_ENTRY') { @@ -141,14 +116,15 @@ exports.postPermission = async function postPermission(req, res, next) { const [existingPermission] = await connection.query(fetchSql, [req.body.userId, req.body.collectionId]); return existingPermission[0]; }); - } - else { - return { error: error.message }; + } else { + return { + error: error.message + }; } } -} +}; -exports.putPermission = async function putPermission(req, res, next) { +exports.putPermission = async function putPermission(userId, elevate, req) { if (!req.body.userId) { return next({ status: 400, @@ -178,22 +154,41 @@ exports.putPermission = async function putPermission(req, res, next) { try { return await withConnection(async (connection) => { - let sql_query = "UPDATE cpat.collectionpermissions SET collectionId = ?, accessLevel = ? WHERE userId = ? AND collectionId = ?;"; - await connection.query(sql_query, [req.body.newCollectionId, req.body.accessLevel, req.body.userId, req.body.oldCollectionId]); + if (elevate) { + const userSql = "SELECT * FROM user WHERE userId = ?"; + const [userRows] = await connection.query(userSql, [userId]); - const message = { - userId: req.body.userId, - collectionId: req.body.newCollectionId, - accessLevel: req.body.accessLevel, - }; - return message; + if (userRows.length === 0) { + throw new SmError.PrivilegeError('User not found'); + } + + const isAdmin = userRows[0].isAdmin; + + if (isAdmin !== 1) { + throw new SmError.PrivilegeError('User requesting Elevate without admin permissions.'); + } + + let sql_query = "UPDATE cpat.collectionpermissions SET collectionId = ?, accessLevel = ? WHERE userId = ? AND collectionId = ?;"; + await connection.query(sql_query, [req.body.newCollectionId, req.body.accessLevel, req.body.userId, req.body.oldCollectionId]); + + const message = { + userId: req.body.userId, + collectionId: req.body.newCollectionId, + accessLevel: req.body.accessLevel, + }; + return message; + } else { + throw new Error('Elevate parameter is required'); + } }); } catch (error) { - return { error: error.message }; + return { + error: error.message + }; } -} +}; -exports.deletePermission = async function deletePermission(req, res, next) { +exports.deletePermission = async function deletePermission(userId, elevate, req) { if (!req.params.userId) { return next({ status: 400, @@ -214,12 +209,33 @@ exports.deletePermission = async function deletePermission(req, res, next) { try { return await withConnection(async (connection) => { - let sql = "DELETE FROM cpat.collectionpermissions WHERE userId = ? AND collectionId = ?"; - await connection.query(sql, [req.params.userId, req.params.collectionId]); + if (elevate) { + const userSql = "SELECT * FROM user WHERE userId = ?"; + const [userRows] = await connection.query(userSql, [userId]); + + if (userRows.length === 0) { + throw new SmError.PrivilegeError('User not found'); + } - return { permissions: [] }; + const isAdmin = userRows[0].isAdmin; + + if (isAdmin !== 1) { + throw new SmError.PrivilegeError('User requesting Elevate without admin permissions.'); + } + + let sql = "DELETE FROM cpat.collectionpermissions WHERE userId = ? AND collectionId = ?"; + await connection.query(sql, [req.params.userId, req.params.collectionId]); + + return { + permissions: [] + }; + } else { + throw new Error('Elevate parameter is required'); + } }); } catch (error) { - return { error: error.message }; + return { + error: error.message + }; } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/api/Services/poamApproverService.js b/api/Services/poamApproverService.js index dc7cd8b6..6724ff97 100644 --- a/api/Services/poamApproverService.js +++ b/api/Services/poamApproverService.js @@ -14,6 +14,7 @@ const dbUtils = require('./utils'); const mysql = require('mysql2'); const usersService = require('./usersService'); const marketplaceService = require('./marketplaceService'); + async function withConnection(callback) { const connection = await dbUtils.pool.getConnection(); try { @@ -84,76 +85,6 @@ exports.getPoamApproversByCollection = async function getPoamApproversByCollecti } } -exports.getPoamApproversByCollectionUser = async function getPoamApproversByCollectionUser(req, res, next) { - if (!req.params.collectionId) { - return next({ - status: 400, - errors: { - collectionId: 'is required', - } - }); - } - if (!req.params.userId) { - return next({ - status: 400, - errors: { - userId: 'is required', - } - }); - } - - try { - return await withConnection(async (connection) => { - let sql = ` - SELECT T1.*, T2.firstName, T2.lastName, T2.fullName, T2.email - FROM cpat.poamapprovers T1 - INNER JOIN cpat.user T2 ON T1.userId = T2.userId - INNER JOIN cpat.poam T3 ON T1.poamId = T3.poamId - WHERE T3.collectionId = ? AND T1.userId = ? - `; - let [rows] = await connection.query(sql, [req.params.collectionId, req.params.userId]); - const poamApprovers = rows.map(row => ({ - ...row, - approvedDate: row.approvedDate ? row.approvedDate.toISOString() : null, - })); - return poamApprovers; - }); - } catch (error) { - return { error: error.message }; - } -} - -exports.getPoamApproversByUserId = async function getPoamApproversByUserId(req, res, next) { - if (!req.params.userId) { - return next({ - status: 400, - errors: { - userId: 'is required', - } - }); - } - - try { - return await withConnection(async (connection) => { - let sql = ` - SELECT T1.*, T2.firstName, T2.lastName, T2.fullName, T2.email - FROM cpat.poamapprovers T1 - INNER JOIN cpat.user T2 ON T1.userId = T2.userId - INNER JOIN cpat.poam T3 ON T1.poamId = T3.poamId - WHERE T1.userId = ? - `; - let [rows] = await connection.query(sql, [req.params.userId]); - const poamApprovers = rows.map(row => ({ - ...row, - approvedDate: row.approvedDate ? row.approvedDate.toISOString() : null, - })); - return poamApprovers; - }); - } catch (error) { - return { error: error.message }; - } -} - exports.postPoamApprover = async function postPoamApprover(req, res, next) { if (!req.body.poamId) { return next({ @@ -251,7 +182,7 @@ exports.putPoamApprover = async function putPoamApprover(req, res, next) { (existingApproval[0].approvalStatus !== req.body.approvalStatus && (req.body.approvalStatus === 'Approved' || req.body.approvalStatus === 'Rejected')); - if (isNewOrChangedApproval) { + if (isNewOrChangedApproval && !config.client.features.marketplaceDisabled) { const approverPoints = await marketplaceService.getUserPoints(req.body.userId); const newApproverPoints = approverPoints.points + 10; await usersService.updateUserPoints({ @@ -295,45 +226,44 @@ exports.putPoamApprover = async function putPoamApprover(req, res, next) { const hqs = poamResult[0].hqs; if (req.body.approvalStatus === 'Approved') { + const userPoints = await marketplaceService.getUserPoints(submitterId); if ((hqs === 0 || hqs === false || hqs === null) && req.body.hqs === true) { - const userHqsPoints = await marketplaceService.getUserPoints(submitterId); - const newHqsPoints = userHqsPoints.points + 15; - await usersService.updateUserPoints({ - body: { - userId: submitterId, - points: newHqsPoints - } - }); - - const hqsNotification = { - title: `High Quality Submission`, - message: `15 points have been added to your points balance for a High Quality submission on POAM ${req.body.poamId}.`, - userId: submitterId - }; - const hqsNotificationSql = `INSERT INTO cpat.notification (userId, title, message) VALUES (?, ?, ?)`; - await connection.query(hqsNotificationSql, [submitterId, hqsNotification.title, hqsNotification.message]); - + if (!config.client.features.marketplaceDisabled) { + const newHqsPoints = userPoints.points + 30; + await usersService.updateUserPoints({ + body: { + userId: submitterId, + points: newHqsPoints + } + }); + const hqsNotification = { + title: `High Quality Submission`, + message: `30 points have been added to your points balance for a High Quality submission on POAM ${req.body.poamId}.`, + userId: submitterId + }; + const hqsNotificationSql = `INSERT INTO cpat.notification (userId, title, message) VALUES (?, ?, ?)`; + await connection.query(hqsNotificationSql, [submitterId, hqsNotification.title, hqsNotification.message]); + } const hqsPoamSql = `UPDATE cpat.poam SET hqs = 1 WHERE poamId = ?`; await connection.query(hqsPoamSql, [req.body.poamId]); + } else if (!config.client.features.marketplaceDisabled) { + const newPoints = userPoints.points + 15; + await usersService.updateUserPoints({ + body: { + userId: submitterId, + points: newPoints + } + }); + + const pointsNotification = { + title: `POAM Approval Points`, + message: `15 points have been added to your points balance for approval of POAM ${req.body.poamId}.`, + userId: submitterId + }; + const pointsNotificationSql = `INSERT INTO cpat.notification (userId, title, message) VALUES (?, ?, ?)`; + await connection.query(pointsNotificationSql, [submitterId, pointsNotification.title, pointsNotification.message]); } - - const userPoints = await marketplaceService.getUserPoints(submitterId); - const newPoints = userPoints.points + 15; - await usersService.updateUserPoints({ - body: { - userId: submitterId, - points: newPoints - } - }); - - const pointsNotification = { - title: `POAM Approval Points`, - message: `15 points have been added to your points balance for approval of POAM ${req.body.poamId}.`, - userId: submitterId - }; - const pointsNotificationSql = `INSERT INTO cpat.notification (userId, title, message) VALUES (?, ?, ?)`; - await connection.query(pointsNotificationSql, [submitterId, pointsNotification.title, pointsNotification.message]); - + let notificationMessage = `POAM ${req.body.poamId} has been approved by ${fullName}.`; if (req.body.hqs === true) { notificationMessage = `POAM ${req.body.poamId} has been approved and marked as High Quality by ${fullName}.`; @@ -359,7 +289,7 @@ exports.putPoamApprover = async function putPoamApprover(req, res, next) { const isApproverInPermissions = permissionResult.some(p => p.userId === req.body.poamLog[0].userId); if (req.body.approvalStatus === 'Approved') { - if (rawSeverity === "CAT I - Critical/High" && !isApproverInPermissions) { + if ((rawSeverity === "CAT I - Critical" || rawSeverity === "CAT I - High") && !isApproverInPermissions) { for (const perm of permissionResult) { const cat1Notification = { title: 'CAT-I POAM Pending Approval', diff --git a/api/Services/poamAssigneeService.js b/api/Services/poamAssigneeService.js index 7fd35345..bff59d68 100644 --- a/api/Services/poamAssigneeService.js +++ b/api/Services/poamAssigneeService.js @@ -67,59 +67,6 @@ exports.getPoamAssigneesByPoamId = async function getPoamAssigneesByPoamId(poamI }); }; -exports.getPoamAssigneesByUserId = async function getPoamAssigneesByUserId(req, res, next) { - if (!req.params.userId) { - throw new Error('getPoamAssigneesByUserId: userId is required'); - } - - return await withConnection(async (connection) => { - let sql = ` - SELECT t1.userId, t2.fullName, t1.poamId, t3.status - FROM cpat.poamassignees t1 - INNER JOIN cpat.user t2 ON t1.userId = t2.userId - INNER JOIN cpat.poam t3 ON t1.poamId = t3.poamId - WHERE t1.userId = ? - ORDER BY t2.fullName - `; - let [rowPoamAssignees] = await connection.query(sql, [req.params.userId]); - const poamAssignees = rowPoamAssignees.map(row => ({ - userId: row.userId, - fullName: row.fullName, - poamId: row.poamId, - poamStatus: row.status, - })); - return { poamAssignees }; - }); -}; - -exports.getPoamAssignee = async function getPoamAssignee(req, res, next) { - if (!req.params.userId) { - throw new Error('getPoamAssignee: userId is required'); - } - if (!req.params.poamId) { - throw new Error('getPoamAssignee: poamId is required'); - } - - return await withConnection(async (connection) => { - let sql = ` - SELECT t1.userId, t2.fullName, t1.poamId, t3.status - FROM cpat.poamassignees t1 - INNER JOIN cpat.user t2 ON t1.userId = t2.userId - INNER JOIN cpat.poam t3 ON t1.poamId = t3.poamId - WHERE t1.userId = ? AND t1.poamId = ? - ORDER BY t2.fullName - `; - let [rowPoamAssignees] = await connection.query(sql, [req.params.userId, req.params.poamId]); - const poamAssignees = rowPoamAssignees.map(row => ({ - userId: row.userId, - fullName: row.fullName, - poamId: row.poamId, - poamStatus: row.status, - })); - const poamAssignee = poamAssignees.length > 0 ? [poamAssignees[0]] : []; - return { poamAssignee }; - }); -}; exports.postPoamAssignee = async function postPoamAssignee(req, res, next) { if (!req.body.userId) { diff --git a/api/Services/poamExtensionService.js b/api/Services/poamExtensionService.js index 14664bb1..8b20a513 100644 --- a/api/Services/poamExtensionService.js +++ b/api/Services/poamExtensionService.js @@ -36,24 +36,55 @@ exports.getPoamExtension = async function (poamId) { exports.putPoamExtension = async function (extensionData) { return withConnection(async (connection) => { - let sql = "UPDATE cpat.poam SET extensionTimeAllowed = ?, extensionJustification = ? WHERE poamId = ?"; - await connection.query(sql, [extensionData.extensionTimeAllowed, extensionData.extensionJustification, extensionData.poamId]); + try { + let sql = `UPDATE cpat.poam SET + extensionTimeAllowed = ?, + extensionJustification = ?, + mitigations = ?, + requiredResources = ?, + residualRisk = ?, + likelihood = ?, + relevanceOfThreat = ?, + impactRating = ?, + impactDescription = ?, + status = ? + WHERE poamId = ?`; - if (extensionData.poamLog && extensionData.poamLog.length > 0) { - let scheduledCompletionDateQuery = `SELECT scheduledCompletionDate FROM cpat.poam WHERE poamId = ?`; - let [[scheduledCompletionDateResult]] = await connection.query(scheduledCompletionDateQuery, [extensionData.poamId]); - if (scheduledCompletionDateResult) { - let scheduledCompletionDate = new Date(scheduledCompletionDateResult.scheduledCompletionDate); - let deadlineWithExtension = addDays(scheduledCompletionDate, extensionData.extensionTimeAllowed); + const params = [ + extensionData.extensionTimeAllowed, + extensionData.extensionJustification, + extensionData.mitigations, + extensionData.requiredResources, + extensionData.residualRisk, + extensionData.likelihood, + extensionData.relevanceOfThreat, + extensionData.impactRating, + extensionData.impactDescription, + extensionData.status, + extensionData.poamId + ]; - let formattedDeadline = deadlineWithExtension.toLocaleDateString("en-US"); - - let action = `POAM Extended. Extension time requested: ${extensionData.extensionTimeAllowed} days
Extension Justification: ${extensionData.extensionJustification}
Deadline with Extension: ${formattedDeadline}`; + const [result] = await connection.query(sql, params); + if (extensionData.poamLog && extensionData.poamLog.length > 0) { + let action = `POAM Updated. Status: ${extensionData.status}`; + if (extensionData.extensionTimeAllowed > 0) { + let scheduledCompletionDateQuery = `SELECT scheduledCompletionDate FROM cpat.poam WHERE poamId = ?`; + let [[scheduledCompletionDateResult]] = await connection.query(scheduledCompletionDateQuery, [extensionData.poamId]); + if (scheduledCompletionDateResult) { + let scheduledCompletionDate = new Date(scheduledCompletionDateResult.scheduledCompletionDate); + let deadlineWithExtension = new Date(scheduledCompletionDate.getTime() + extensionData.extensionTimeAllowed * 24 * 60 * 60 * 1000); + let formattedDeadline = deadlineWithExtension.toLocaleDateString("en-US"); + action += `
Extension time requested: ${extensionData.extensionTimeAllowed} days
Extension Justification: ${extensionData.extensionJustification}
Deadline with Extension: ${formattedDeadline}`; + } + } let logSql = "INSERT INTO cpat.poamlogs (poamId, action, userId) VALUES (?, ?, ?)"; await connection.query(logSql, [extensionData.poamId, action, extensionData.poamLog[0].userId]); } + + return extensionData; + } catch (error) { + throw error; } - return extensionData; }); }; diff --git a/api/Services/poamLabelService.js b/api/Services/poamLabelService.js index 02441322..02722a43 100644 --- a/api/Services/poamLabelService.js +++ b/api/Services/poamLabelService.js @@ -22,6 +22,15 @@ async function withConnection(callback) { } } +async function withConnection(callback) { + const connection = await dbUtils.pool.getConnection(); + try { + return await callback(connection); + } finally { + await connection.release(); + } +} + exports.getPoamLabels = async function getPoamLabels(collectionId) { try { if (!collectionId) { @@ -55,19 +64,8 @@ exports.getPoamLabels = async function getPoamLabels(collectionId) { } } -exports.getAvailablePoamLabels = async function getAvailablePoamLabels(req, res, next) { +exports.getAvailablePoamLabels = async function getAvailablePoamLabels(userId) { try { - const userId = req.params.userId; - - if (!req.params.userId) { - return next({ - status: 400, - errors: { - userId: 'is required', - } - }); - } - return await withConnection(async (connection) => { const [adminRows] = await connection.query("SELECT isAdmin FROM cpat.user WHERE userId = ?", [userId]); const isAdmin = adminRows[0].isAdmin; diff --git a/api/Services/poamService.js b/api/Services/poamService.js index 15fde6ce..ab386680 100644 --- a/api/Services/poamService.js +++ b/api/Services/poamService.js @@ -25,17 +25,17 @@ async function withConnection(callback) { } } + + function normalizeDate(date) { if (!date) return null; const d = new Date(date); return d.toISOString().split('T')[0]; } -exports.getAvailablePoams = async function getAvailablePoams(req, res, next) { +exports.getAvailablePoams = async function getAvailablePoams(userId, req) { try { return await withConnection(async (connection) => { - const userId = req.params.userId; - const [adminRows] = await connection.query("SELECT isAdmin FROM cpat.user WHERE userId = ?", [userId]); const isAdmin = adminRows[0].isAdmin; @@ -266,8 +266,8 @@ exports.postPoam = async function postPoam(req) { let sql_query = `INSERT INTO cpat.poam (collectionId, vulnerabilitySource, stigTitle, stigBenchmarkId, stigCheckData, tenablePluginData, iavmNumber, aaPackage, vulnerabilityId, description, rawSeverity, adjSeverity, iavComplyByDate, scheduledCompletionDate, submitterId, officeOrg, predisposingConditions, mitigations, requiredResources, residualRisk, likelihood, relevanceOfThreat, - businessImpactRating, businessImpactDescription, status, vulnIdRestricted, submittedDate, closedDate) - values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`; + impactRating, impactDescription, status, submittedDate, closedDate) + values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`; try { return await withConnection(async (connection) => { @@ -284,8 +284,8 @@ exports.postPoam = async function postPoam(req) { await connection.query(sql_query, [req.body.collectionId, req.body.vulnerabilitySource, req.body.stigTitle, req.body.stigBenchmarkId, req.body.stigCheckData, req.body.tenablePluginData, req.body.iavmNumber, req.body.aaPackage, req.body.vulnerabilityId, req.body.description, req.body.rawSeverity, req.body.adjSeverity, req.body.iavComplyByDate, req.body.scheduledCompletionDate, req.body.submitterId, req.body.officeOrg, req.body.predisposingConditions, req.body.mitigations, - req.body.requiredResources, req.body.residualRisk, req.body.likelihood, req.body.relevanceOfThreat, req.body.businessImpactRating, req.body.businessImpactDescription, - req.body.status, req.body.vulnIdRestricted, req.body.submittedDate, req.body.closedDate]); + req.body.requiredResources, req.body.residualRisk, req.body.likelihood, req.body.relevanceOfThreat, req.body.impactRating, req.body.impactDescription, + req.body.status, req.body.submittedDate, req.body.closedDate]); let sql = "SELECT * FROM cpat.poam WHERE poamId = LAST_INSERT_ID();"; let [rowPoam] = await connection.query(sql); @@ -389,12 +389,11 @@ exports.putPoam = async function putPoam(req, res, next) { "requiredResources": "Required Resources", "residualRisk": "Residual Risk", "status": "POAM Status", - "vulnIdRestricted": "Vulnerability Restricted ID #", "submittedDate": "Submitted Date", "likelihood": "Likelihood", "relevanceOfThreat": "Relevance of Threat", - "businessImpactRating": "Business Impact", - "businessImpactDescription": "Business Impact Description" + "impactRating": "Impact", + "impactDescription": "Impact Description" }; try { @@ -423,15 +422,15 @@ exports.putPoam = async function putPoam(req, res, next) { const sqlInsertPoam = `UPDATE cpat.poam SET collectionId = ?, vulnerabilitySource = ?, stigTitle = ?, stigBenchmarkId = ?, stigCheckData = ?, tenablePluginData = ?, iavmNumber = ?, aaPackage = ?, vulnerabilityId = ?, description = ?, rawSeverity = ?, adjSeverity = ?, iavComplyByDate = ?, scheduledCompletionDate = ?, submitterId = ?, predisposingConditions = ?, mitigations = ?, requiredResources = ?, - residualRisk = ?, likelihood = ?, relevanceOfThreat = ?, businessImpactRating = ?, businessImpactDescription = ?, status = ?, - vulnIdRestricted = ?, submittedDate = ?, closedDate = ?, officeOrg = ? WHERE poamId = ?`; + residualRisk = ?, likelihood = ?, relevanceOfThreat = ?, impactRating = ?, impactDescription = ?, status = ?, + submittedDate = ?, closedDate = ?, officeOrg = ? WHERE poamId = ?`; await connection.query(sqlInsertPoam, [ req.body.collectionId, req.body.vulnerabilitySource, req.body.stigTitle, req.body.stigBenchmarkId, req.body.stigCheckData, req.body.tenablePluginData, req.body.iavmNumber, req.body.aaPackage, req.body.vulnerabilityId, req.body.description, req.body.rawSeverity, req.body.adjSeverity, req.body.iavComplyByDate, req.body.scheduledCompletionDate, req.body.submitterId, req.body.predisposingConditions, req.body.mitigations, req.body.requiredResources, req.body.residualRisk, req.body.likelihood, req.body.relevanceOfThreat, - req.body.businessImpactRating, req.body.businessImpactDescription, req.body.status, req.body.vulnIdRestricted, + req.body.impactRating, req.body.impactDescription, req.body.status, req.body.submittedDate, req.body.closedDate, req.body.officeOrg, req.body.poamId ]); diff --git a/api/Services/usersService.js b/api/Services/usersService.js index 82348cf7..8afe48dd 100644 --- a/api/Services/usersService.js +++ b/api/Services/usersService.js @@ -10,7 +10,7 @@ const dbUtils = require('./utils'); const logger = require('../utils/logger'); -const _this = this +const SmError = require('../utils/error'); async function withConnection(callback) { const connection = await dbUtils.pool.getConnection(); @@ -21,54 +21,71 @@ async function withConnection(callback) { } } -exports.getUsers = async function getUsers() { +exports.getUsers = async function getUsers(elevate, userId) { try { return await withConnection(async (connection) => { - let sql = "SELECT * FROM cpat.user;"; - let [rows] = await connection.query(sql); - - const users = await Promise.all(rows.map(async (user) => { - const sqlPermissions = "SELECT * FROM cpat.collectionpermissions WHERE userId = ?"; - const [permissionRows] = await connection.query(sqlPermissions, [user.userId]); - - const permissions = permissionRows.map(permission => ({ - userId: permission.userId, - collectionId: permission.collectionId, - accessLevel: permission.accessLevel, + if (elevate) { + const userSql = "SELECT * FROM user WHERE userId = ?"; + const [userRows] = await connection.query(userSql, [userId]); + + if (userRows.length === 0) { + throw new SmError.PrivilegeError('User not found'); + } + + const isAdmin = userRows[0].isAdmin; + + if (isAdmin !== 1) { + throw new SmError.PrivilegeError('User requesting Elevate without admin permissions.'); + } + + const allUsersSql = "SELECT * FROM cpat.user"; + const [allUsersRows] = await connection.query(allUsersSql); + + const users = await Promise.all(allUsersRows.map(async (user) => { + const permissionsSql = "SELECT * FROM cpat.collectionpermissions WHERE userId = ?"; + const [permissionRows] = await connection.query(permissionsSql, [user.userId]); + + const permissions = permissionRows.map(permission => ({ + userId: permission.userId, + collectionId: permission.collectionId, + accessLevel: permission.accessLevel, + })); + + return { + userId: user.userId, + userName: user.userName, + email: user.email, + firstName: user.firstName, + lastName: user.lastName, + created: user.created, + lastAccess: user.lastAccess, + lastCollectionAccessedId: user.lastCollectionAccessedId, + accountStatus: user.accountStatus, + fullName: user.fullName, + officeOrg: user.officeOrg, + defaultTheme: user.defaultTheme, + points: user.points, + isAdmin: user.isAdmin, + lastClaims: user.lastClaims, + permissions: permissions + }; })); - return { - userId: user.userId, - userName: user.userName, - email: user.email, - firstName: user.firstName, - lastName: user.lastName, - created: user.created, - lastAccess: user.lastAccess, - lastCollectionAccessedId: user.lastCollectionAccessedId, - accountStatus: user.accountStatus, - fullName: user.fullName, - officeOrg: user.officeOrg, - defaultTheme: user.defaultTheme, - points: user.points, - isAdmin: user.isAdmin, - lastClaims: user.lastClaims, - permissions: permissions - }; - })); - - return users; + return users; + } else { + throw new Error('Elevate parameter is required'); + } }); } catch (error) { return { error: error.message }; } }; -exports.getCurrentUser = async function getCurrentUser(email) { +exports.getCurrentUser = async function getCurrentUser(userId) { try { return await withConnection(async (connection) => { - const sqlUser = "SELECT * FROM user WHERE email = ?"; - const [userRows] = await connection.query(sqlUser, [email]); + const sqlUser = "SELECT * FROM user WHERE userId = ?"; + const [userRows] = await connection.query(sqlUser, [userId]); if (userRows.length === 0) { return userRows[0]; @@ -85,7 +102,7 @@ exports.getCurrentUser = async function getCurrentUser(email) { accessLevel: permission.accessLevel, })); - return { + const userObject = { userId: user.userId, userName: user.userName, email: user.email, @@ -102,52 +119,70 @@ exports.getCurrentUser = async function getCurrentUser(email) { isAdmin: user.isAdmin, lastClaims: user.lastClaims, permissions: permissions - }; + }; + return userObject; }); } catch (error) { return { error: error.message }; } }; -exports.getUserByUserID = async function getUserByUserID(userId) { +exports.getUserByUserID = async function getUserByUserID(requestorId, elevate, userId) { try { return await withConnection(async (connection) => { - let sql = "SELECT * FROM user WHERE userId = ?"; - const [userRows] = await connection.query(sql, [userId]); + if (elevate) { + const userSql = "SELECT * FROM user WHERE userId = ?"; + const [userRows] = await connection.query(userSql, [requestorId]); - if (userRows.length === 0) { - return userRows[0]; - } + if (userRows.length === 0) { + throw new SmError.PrivilegeError('User not found'); + } - const user = userRows[0]; + const isAdmin = userRows[0].isAdmin; - const sqlPermissions = "SELECT * FROM cpat.collectionpermissions WHERE userId = ?"; - const [permissionRows] = await connection.query(sqlPermissions, [user.userId]); + if (isAdmin !== 1) { + throw new SmError.PrivilegeError('User requesting Elevate without admin permissions.'); + } - const permissions = permissionRows.map(permission => ({ - userId: permission.userId, - collectionId: permission.collectionId, - accessLevel: permission.accessLevel, - })); + let sql = "SELECT * FROM user WHERE userId = ?"; + const [userQueryRows] = await connection.query(sql, [userId]); - return { - userId: user.userId, - userName: user.userName, - email: user.email, - firstName: user.firstName, - lastName: user.lastName, - created: user.created, - lastAccess: user.lastAccess, - lastCollectionAccessedId: user.lastCollectionAccessedId, - accountStatus: user.accountStatus, - fullName: user.fullName, - officeOrg: user.officeOrg, - defaultTheme: user.defaultTheme, - points: user.points, - isAdmin: user.isAdmin, - lastClaims: user.lastClaims, - permissions: permissions - }; + if (userQueryRows.length === 0) { + return userQueryRows[0]; + } + + const user = userQueryRows[0]; + + const sqlPermissions = "SELECT * FROM cpat.collectionpermissions WHERE userId = ?"; + const [permissionRows] = await connection.query(sqlPermissions, [user.userId]); + + const permissions = permissionRows.map(permission => ({ + userId: permission.userId, + collectionId: permission.collectionId, + accessLevel: permission.accessLevel, + })); + + return { + userId: user.userId, + userName: user.userName, + email: user.email, + firstName: user.firstName, + lastName: user.lastName, + created: user.created, + lastAccess: user.lastAccess, + lastCollectionAccessedId: user.lastCollectionAccessedId, + accountStatus: user.accountStatus, + fullName: user.fullName, + officeOrg: user.officeOrg, + defaultTheme: user.defaultTheme, + points: user.points, + isAdmin: user.isAdmin, + lastClaims: user.lastClaims, + permissions: permissions + }; + } else { + throw new Error('Elevate parameter is required'); + } }); } catch (error) { return { error: error.message }; @@ -200,36 +235,24 @@ exports.getUserByUserName = async function getUserByUserName(userName) { } }; -exports.getBasicUserByUserID = async function getBasicUserByUserID(userId) { +exports.updateUser = async function updateUser(userId, elevate, req) { try { return await withConnection(async (connection) => { - let sql = "SELECT * FROM user WHERE userId = ?"; - const [userRows] = await connection.query(sql, [userId]); + if (elevate) { + const userSql = "SELECT * FROM user WHERE userId = ?"; + const [userRows] = await connection.query(userSql, [userId]); - if (userRows.length === 0) { - return userRows[0]; - } + if (userRows.length === 0) { + throw new SmError.PrivilegeError('User not found'); + } - const user = userRows[0]; + const isAdmin = userRows[0].isAdmin; - return { - userId: user.userId, - email: user.email, - firstName: user.firstName, - lastName: user.lastName, - fullName: user.fullName, - officeOrg: user.officeOrg, - }; - }); - } catch (error) { - return { error: error.message }; - } -}; + if (isAdmin !== 1) { + throw new SmError.PrivilegeError('User requesting Elevate without admin permissions.'); + } -exports.updateUser = async function updateUser(req, res, next) { - try { - return await withConnection(async (connection) => { - let sql = "UPDATE user SET firstName = ?, lastName = ?, email = ?, lastAccess = ?, lastCollectionAccessedId = ?, accountStatus = ?, fullName = ?, officeOrg = ?, defaultTheme = ?, isAdmin = ? WHERE userId = ?"; + let sql = "UPDATE user SET firstName = ?, lastName = ?, email = ?, lastAccess = ?, lastCollectionAccessedId = ?, accountStatus = ?, fullName = ?, officeOrg = ?, defaultTheme = ?, isAdmin = ?, points = ? WHERE userId = ?"; await connection.query(sql, [ req.body.firstName, @@ -242,6 +265,7 @@ exports.updateUser = async function updateUser(req, res, next) { req.body.officeOrg, req.body.defaultTheme, req.body.isAdmin, + req.body.points, req.body.userId ]); @@ -249,6 +273,9 @@ exports.updateUser = async function updateUser(req, res, next) { let [updatedUser] = await connection.query(sql, [req.body.userId]); return updatedUser[0]; + } else { + throw new Error('Elevate parameter is required'); + } }); } catch (error) { return { error: error.message }; @@ -272,7 +299,7 @@ exports.updateUserTheme = async function updateUserTheme(req, res, next) { } }; -exports.updateUserPoints = async function updateUserPoints(req, res, next) { +exports.updateUserPoints = async function updateUserPoints(elevate, req) { try { return await withConnection(async (connection) => { let sql = "UPDATE user SET points = ? WHERE userId = ?"; @@ -289,35 +316,50 @@ exports.updateUserPoints = async function updateUserPoints(req, res, next) { } }; -exports.setUserData = async function setUserData(userObject) { +exports.setUserData = async function setUserData(userObject, fields, newUser) { try { return await withConnection(async (connection) => { - let insertColumns = ['userName', 'email', 'firstName', 'lastName', 'fullName', 'lastClaims', 'isAdmin']; - let updateColumns = ['userId = LAST_INSERT_ID(userId)', 'firstName = VALUES(firstName)', 'lastName = VALUES(lastName)', 'fullName = VALUES(fullName)', 'lastClaims = VALUES(lastClaims)', 'isAdmin = VALUES(isAdmin)']; - - let lastClaimsSerialized = JSON.stringify(userObject.lastClaims); - - let binds = [userObject.userName, userObject.email, userObject.firstName, userObject.lastName, userObject.fullName, lastClaimsSerialized, userObject.isAdmin]; - - let sql = ` - INSERT INTO cpat.user (${insertColumns.join(', ')}) - VALUES (${insertColumns.map(() => '?').join(', ')}) - ON DUPLICATE KEY UPDATE ${updateColumns.join(', ')} - `; - const [result] = await connection.query(sql, binds); - let userId = userObject.userId; - if (!userId && result.insertId) { - userId = result.insertId; + console.log("setUserData!@!@!@", userObject, fields, newUser); + let insertColumns = ['userName'] + let updateColumns = ['userId = LAST_INSERT_ID(userId)'] + let binds = [userObject.userName] + + if (newUser && userObject.firstName) { + insertColumns.push('firstName') + updateColumns.push('firstName = VALUES(firstName)') + binds.push(userObject.firstName) } - - sql = "SELECT * FROM user WHERE userId = ?"; - let [updatedUser] = await connection.query(sql, [userId]); - - if (updatedUser[0]) { - updatedUser[0].lastClaims = JSON.parse(updatedUser[0].lastClaims); + if (newUser && userObject.lastName) { + insertColumns.push('lastName') + updateColumns.push('lastName = VALUES(lastName)') + binds.push(userObject.lastName) } - - return updatedUser[0]; + if (newUser && userObject.fullName) { + insertColumns.push('fullName') + updateColumns.push('fullName = VALUES(fullName)') + binds.push(userObject.fullName) + } + if (newUser && userObject.email) { + insertColumns.push('email') + updateColumns.push('email = VALUES(email)') + binds.push(userObject.email) + } + if (fields.lastAccess) { + insertColumns.push('lastAccess') + updateColumns.push('lastAccess = VALUES(lastAccess)') + binds.push(fields.lastAccess) + } + if (fields.lastClaims) { + insertColumns.push('lastClaims') + updateColumns.push('lastClaims = VALUES(lastClaims)') + binds.push(JSON.stringify(fields.lastClaims)) + } + let sqlUpsert = `INSERT INTO cpat.user ( + ${insertColumns.join(',\n')} + ) VALUES ? ON DUPLICATE KEY UPDATE + ${updateColumns.join(',\n')}` + let [result] = await connection.query(sqlUpsert, [[binds]]) + return result; }); } catch (error) { return { error: error.message }; @@ -337,9 +379,23 @@ exports.setLastAccess = async function (userId, timestamp) { } }; -exports.deleteUserByUserID = async function deleteUserByUserID(userId, username, displayName) { +exports.deleteUser = async function deleteUser(requestorId, elevate, userId) { try { return await withConnection(async (connection) => { + if (elevate) { + const userSql = "SELECT * FROM user WHERE userId = ?"; + const [userRows] = await connection.query(userSql, [requestorId]); + + if (userRows.length === 0) { + throw new SmError.PrivilegeError('User not found'); + } + + const isAdmin = userRows[0].isAdmin; + + if (isAdmin !== 1) { + throw new SmError.PrivilegeError('User requesting Elevate without admin permissions.'); + } + let sql = 'DELETE FROM `user` WHERE `userId`= ?'; await connection.query(sql, [userId]); @@ -347,34 +403,11 @@ exports.deleteUserByUserID = async function deleteUserByUserID(userId, username, logger.writeInfo("usersService", 'notification', { event: 'removed account', userId: userId }); return { message: "User deleted" }; + } else { + throw new Error('Elevate parameter is required'); + } }); } catch (error) { return { error: error.message }; } -}; - -exports.loginState = async function loginState(req, res, next) { - let message = { message: "" }; - if (req.body.loginState == 'logIn') { - logger.writeInfo("usersService", 'info', { event: 'Logged In' }); - message.message = "Login Success"; - } else { - logger.writeInfo("usersService", 'info', { event: 'Logged Out' }); - message.message = "Logout Success"; - } - return message; -}; - -module.exports.deleteUserByUserID = async function deleteUserByUserID(userId, req, res, next) { - return await withConnection(async (connection) => { - let sql_query = 'DELETE FROM `user` WHERE `id`=' + userId; - await connection.query(sql_query); - - logger.writeInfo("usersService", 'log', { event: 'removed account', userId: userId }); - logger.writeInfo("usersService", 'notification', { event: 'removed account', userId: userId }); - - let messageReturned = new Object(); - messageReturned.text = "User deleted"; - return messageReturned; - }); }; \ No newline at end of file diff --git a/api/package-lock.json b/api/package-lock.json index aeb547a4..c060cc97 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -25,7 +25,7 @@ "lodash": "^4.17.21", "multer": "^1.4.5-lts.1", "mysql2": "^3.11.0", - "npm-check-updates": "^17.0.6", + "npm-check-updates": "^17.1.0", "on-finished": "^2.4.1", "on-headers": "^1.0.2", "passport": "^0.7.0", @@ -5268,9 +5268,9 @@ } }, "node_modules/npm-check-updates": { - "version": "17.0.6", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-17.0.6.tgz", - "integrity": "sha512-KCiaJH1cfnh/RyzKiDNjNfXgcKFyQs550Uf1OF/Yzb8xO56w+RLpP/OKRUx23/GyP/mLYwEpOO65qjmVdh6j0A==", + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-17.1.0.tgz", + "integrity": "sha512-RcohCA/tdpxyPllBlYDkqGXFJQgTuEt0f2oPSL9s05pZ3hxYdleaUtvEcSxKl0XAg3ncBhVgLAxhXSjoryUU5Q==", "license": "Apache-2.0", "bin": { "ncu": "build/cli.js", @@ -7005,4 +7005,4 @@ } } } -} \ No newline at end of file +} diff --git a/api/package.json b/api/package.json index f5338041..4aac906c 100644 --- a/api/package.json +++ b/api/package.json @@ -25,7 +25,7 @@ "lodash": "^4.17.21", "multer": "^1.4.5-lts.1", "mysql2": "^3.11.0", - "npm-check-updates": "^17.0.6", + "npm-check-updates": "^17.1.0", "on-finished": "^2.4.1", "on-headers": "^1.0.2", "passport": "^0.7.0", diff --git a/api/specification/C-PAT.yaml b/api/specification/C-PAT.yaml index dc6488f3..cb59d8ba 100644 --- a/api/specification/C-PAT.yaml +++ b/api/specification/C-PAT.yaml @@ -652,14 +652,14 @@ paths: schema: $ref: '#/components/schemas/error' - '/collections/{userName}': + '/collections': get: tags: - Collection summary: Get a list of collections accessible to the user operationId: getCollections parameters: - - $ref: '#/components/parameters/collectionAuth' + - $ref: '#/components/parameters/ElevateQuery' responses: '200': description: Collection list retrieved successfully @@ -675,6 +675,9 @@ paths: application/json: schema: $ref: '#/components/schemas/error' + security: + - oauth: + - 'c-pat:read' /collection: post: @@ -738,6 +741,206 @@ paths: tags: - Collection + /iav/vramUpdatedDate: + get: + summary: Get VRAM data updated date + description: Retrieves the last updated date of the VRAM data + operationId: getVramDataUpdatedDate + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + value: + type: string + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/error' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/error' + tags: + - IAV + security: + - oauth: + - 'c-pat:op' + + /iav/iavSummary: + get: + summary: Get IAV Summary + description: Retrieves IAV table data + operationId: getIAVTableData + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/IAVTableData' + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/error' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/error' + tags: + - IAV + security: + - oauth: + - 'c-pat:read' + + /iav/pluginInfo: + get: + summary: Get IAV and Navy Comply Date for Plugin IDs + description: Retrieves IAV and Navy Comply Date information for given Plugin IDs + operationId: getIAVInfoForPlugins + parameters: + - in: query + name: pluginIDs + required: true + schema: + type: string + description: Comma-separated list of Plugin IDs + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: array + items: + type: object + properties: + pluginID: + type: integer + iav: + type: string + navyComplyDate: + type: string + format: date + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/error' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/error' + tags: + - IAV + security: + - oauth: + - 'c-pat:read' + + /iav/pluginIDs: + get: + summary: Retrieves mapped IAV pluginIDs + description: Get all unique pluginIDs that have an associated IAV as a comma-separated string + operationId: getIAVPluginIds + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + pluginIDs: + type: string + description: Comma-separated list of unique plugin IDs + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/error' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/error' + tags: + - IAV + security: + - oauth: + - 'c-pat:read' + + /mapPluginIds: + post: + summary: Maps Plugin IDs to IAVs + description: Maps Plugin IDs to IAVs + operationId: mapIAVPluginIds + requestBody: + required: true + content: + application/json: + schema: + type: array + items: + type: object + properties: + iav: + type: string + pattern: '^\d{4}-[A-Z]-\d{4}$' + description: IAV in the format 0000-A-0000 + pluginID: + type: string + description: Comma-separated list of plugin IDs + required: + - iav + - pluginID + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + message: + type: string + updatedCount: + type: integer + '400': + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/error' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/error' + tags: + - IAV + security: + - oauth: + - 'c-pat:op' + '/import/poams': post: summary: Parse and import .xls, .xlsx, or .xlsm eMASS POAM exports @@ -1066,17 +1269,10 @@ paths: - oauth: - 'c-pat:read' - /marketplace/user-themes/{userId}: + /marketplace/user-themes: get: - summary: Get all purchased themes for a user + summary: Get all purchased themes operationId: getUserThemes - parameters: - - in: path - name: userId - required: true - schema: - type: integer - description: The ID of the user responses: '200': description: Successfully retrieved purchased themes @@ -1096,17 +1292,10 @@ paths: - oauth: - 'c-pat:read' - /marketplace/user-points/{userId}: + /marketplace/user-points: get: - summary: Get the available points of a user + summary: Get available points balance operationId: getUserPoints - parameters: - - in: path - name: userId - required: true - schema: - type: integer - description: The ID of the user responses: '200': description: Successfully retrieved purchased themes @@ -1288,9 +1477,7 @@ paths: - oauth: - 'c-pat:read' - '/metrics/available/{userId}/assetlabel': - parameters: - - $ref: '#/components/parameters/userIdPath' + '/metrics/available/assetlabel': get: summary: Get all asset labels and label counts available to the user operationId: getAvailableAssetLabel @@ -1315,9 +1502,7 @@ paths: - oauth: - 'c-pat:op' - '/metrics/available/{userId}/poamlabel': - parameters: - - $ref: '#/components/parameters/userIdPath' + '/metrics/available/poamlabel': get: summary: Get all POAM labels and label counts available to the user operationId: getAvailablePoamLabel @@ -1342,9 +1527,7 @@ paths: - oauth: - 'c-pat:op' - '/metrics/available/{userId}/poamstatus': - parameters: - - $ref: '#/components/parameters/userIdPath' + '/metrics/available/poamstatus': get: summary: Get all POAM status and status counts available to the user operationId: getAvailablePoamStatus @@ -1369,9 +1552,7 @@ paths: - oauth: - 'c-pat:op' - '/metrics/available/{userId}/poamseverity': - parameters: - - $ref: '#/components/parameters/userIdPath' + '/metrics/available/poamseverity': get: summary: Get all POAM severity and severity counts available to the user operationId: getAvailablePoamSeverity @@ -1396,9 +1577,7 @@ paths: - oauth: - 'c-pat:op' - '/metrics/available/{userId}/monthlypoamseverity': - parameters: - - $ref: '#/components/parameters/userIdPath' + '/metrics/available/monthlypoamseverity': get: summary: Get all POAM severity and severity counts from within the last 30 days available to the user operationId: getAvailableMonthlyPoamSeverity @@ -1423,9 +1602,7 @@ paths: - oauth: - 'c-pat:op' - '/metrics/available/{userId}/monthlypoamstatus': - parameters: - - $ref: '#/components/parameters/userIdPath' + '/metrics/available/monthlypoamstatus': get: summary: Get a status of all POAMs available to the user with status and status counts grouped under Open or Closed from POAMs submitted within the last 30 days operationId: getAvailableMonthlyPoamStatus @@ -1450,9 +1627,7 @@ paths: - oauth: - 'c-pat:op' - '/metrics/available/{userId}/poamScheduledCompletion': - parameters: - - $ref: '#/components/parameters/userIdPath' + '/metrics/available/poamScheduledCompletion': get: summary: Get all POAM scheduled completion and scheduled completion counts available to the user operationId: getAvailablePoamScheduledCompletion @@ -1477,9 +1652,7 @@ paths: - oauth: - 'c-pat:op' - '/metrics/available/{userId}/collectionpoamcount': - parameters: - - $ref: '#/components/parameters/userIdPath' + '/metrics/available/collectionpoamcount': get: summary: Get a count of poams for each collection available to the user operationId: getAvailableCollectionPoamCounts @@ -1504,16 +1677,10 @@ paths: - oauth: - 'c-pat:op' - /notifications/all/{userId}: - parameters: - - in: path - name: userId - required: true - schema: - type: integer + /notifications/all: get: summary: Get all notifications for a user - operationId: getAllNotificationsByUserId + operationId: getAllNotifications responses: '200': description: Notification array response @@ -1535,16 +1702,10 @@ paths: - oauth: - 'c-pat:read' - /notifications/unread/{userId}: - parameters: - - in: path - name: userId - required: true - schema: - type: integer + /notifications/unread: get: - summary: Get all unread notifications for a user - operationId: getUnreadNotificationsByUserId + summary: Get all unread notifications + operationId: getUnreadNotifications responses: '200': description: Notification array response @@ -1566,16 +1727,10 @@ paths: - oauth: - 'c-pat:read' - /notifications/unread/count/{userId}: - parameters: - - in: path - name: userId - required: true - schema: - type: integer + /notifications/unread/count: get: - summary: Get a count of all unread notifications for a user - operationId: getUnreadNotificationCountByUserId + summary: Get a count of all unread notifications + operationId: getUnreadNotificationCount responses: '200': description: Notification count response @@ -1604,7 +1759,7 @@ paths: type: integer delete: summary: Delete a notification - operationId: deleteNotificationByNotificationId + operationId: deleteNotification responses: '204': description: Notification dismissed successfully @@ -1626,16 +1781,10 @@ paths: - oauth: - 'c-pat:read' - /notifications/all/delete/{userId}: - parameters: - - in: path - name: userId - required: true - schema: - type: integer + /notifications/all/delete: delete: - summary: Delete a notification - operationId: deleteAllNotificationsByUserId + summary: Deletes all notifications + operationId: deleteAllNotifications responses: '204': description: Notification dismissed successfully @@ -1665,8 +1814,8 @@ paths: schema: type: integer put: - summary: Dismiss a notification - operationId: dismissNotificationByNotificationId + summary: Dismiss an individual notification + operationId: dismissNotification responses: '200': description: Notification array response @@ -1694,16 +1843,10 @@ paths: - oauth: - 'c-pat:read' - /notifications/all/dismiss/{userId}: - parameters: - - in: path - name: userId - required: true - schema: - type: integer + /notifications/all/dismiss: put: - summary: Dismiss all notifications for a user - operationId: dismissAllNotificationsByUserId + summary: Dismiss all notifications + operationId: dismissAllNotifications responses: '204': description: Notification dismissed successfully @@ -1725,36 +1868,76 @@ paths: - oauth: - 'c-pat:read' - '/collection/{collectionId}/user/{userName}': - parameters: - - $ref: '#/components/parameters/collectionIdPath' - - in: path - name: userName - required: true - schema: - type: string + /op/configuration: get: + summary: Return API version and classification + operationId: getConfiguration + responses: + '200': + description: Configuration response + content: + application/json: + schema: + $ref: '#/components/schemas/ApiConfiguration' tags: - - Permissions - summary: Return collection permissions for a user - operationId: getCollectionPermission + - Operation + security: [] + post: + summary: Set a configuration item + operationId: setConfigurationItem + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ConfigurationItem' responses: '200': - description: Collection retrieved successfully + description: Configuration item updated successfully content: application/json: schema: - $ref: '#/components/schemas/collection' - '204': - description: No Content - '500': - description: Internal server error + $ref: '#/components/schemas/SuccessMessage' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/error' + tags: + - Operation + security: + - oauth: + - 'c-pat:op' + delete: + summary: Delete a configuration item + operationId: deleteConfigurationItem + parameters: + - name: key + in: query + required: true + schema: + $ref: '#/components/schemas/String255' + responses: + '200': + description: Configuration item deleted successfully + content: + application/json: + schema: + $ref: '#/components/schemas/SuccessMessage' + '400': + description: Bad request content: application/json: schema: $ref: '#/components/schemas/error' + tags: + - Operation + security: + - oauth: + - 'c-pat:op' - '/collection/permissions/{collectionId}': + '/permissions/{collectionId}': parameters: - in: path name: collectionId @@ -1762,7 +1945,7 @@ paths: schema: type: integer get: - summary: Get all permissions for a specific collection + summary: Get the permission assignments of a collection by collectionId operationId: getCollectionPermissions responses: '200': @@ -1790,6 +1973,7 @@ paths: '/permission/{userId}/{collectionId}': parameters: + - $ref: '#/components/parameters/ElevateQuery' - in: path name: userId required: true @@ -1828,6 +2012,8 @@ paths: post: summary: Add a user collection permission operationId: postPermission + parameters: + - $ref: '#/components/parameters/ElevateQuery' requestBody: required: true content: @@ -1862,6 +2048,8 @@ paths: put: summary: Update user collection permissions operationId: putPermission + parameters: + - $ref: '#/components/parameters/ElevateQuery' requestBody: required: true content: @@ -1930,43 +2118,6 @@ paths: - oauth: - 'c-pat:read' - '/poamApprovers/user/{userId}': - parameters: - - in: path - name: userId - required: true - schema: - type: integer - get: - summary: Return poamApprovers by userId - operationId: getPoamApproversByUserId - responses: - '200': - description: Poam Approvers array response - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/poamApprover_Response' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/error' - default: - description: Unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/error' - tags: - - PoamApprover - security: - - oauth: - - 'c-pat:op' - '/poamApprovers/collection/{collectionId}': parameters: - in: path @@ -2004,48 +2155,6 @@ paths: - oauth: - 'c-pat:op' - '/poamApprovers/collection/{collectionId}/user/{userId}': - parameters: - - in: path - name: collectionId - required: true - schema: - type: integer - - in: path - name: userId - required: true - schema: - type: integer - get: - summary: Return poamApprovers by collectionId and userId - operationId: getPoamApproversByCollectionUser - responses: - '200': - description: Poam Approvers array response - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/poamApprover_Response' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/error' - default: - description: Unexpected error - content: - application/json: - schema: - $ref: '#/components/schemas/error' - tags: - - PoamApprover - security: - - oauth: - - 'c-pat:op' - '/poamApprover/poam/{poamId}/user/{userId}': parameters: - in: path @@ -2402,78 +2511,7 @@ paths: - oauth: - 'c-pat:read' - /poamAssignees/user/{userId}: - get: - summary: Get POAM assignees by user ID - operationId: getPoamAssigneesByUserId - parameters: - - name: userId - in: path - required: true - schema: - type: integer - responses: - '200': - description: Successful response with POAM assignees - content: - application/json: - schema: - type: object - properties: - poamAssignees: - type: array - items: - $ref: '#/components/schemas/poamAssignee_Object' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/error' - tags: - - PoamAssignees - security: - - oauth: - - 'c-pat:read' - /poamAssignee/poam/{poamId}/user/{userId}: - get: - summary: Get a specific POAM assignee by POAM ID and user ID - operationId: getPoamAssignee - parameters: - - name: poamId - in: path - required: true - schema: - type: integer - - name: userId - in: path - required: true - schema: - type: integer - responses: - '200': - description: Successful response with the POAM assignee - content: - application/json: - schema: - type: object - properties: - poamAssignee: - type: array - items: - $ref: '#/components/schemas/poamAssignee_Object' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/error' - tags: - - PoamAssignees - security: - - oauth: - - 'c-pat:read' delete: summary: Delete a POAM assignee operationId: deletePoamAssignee @@ -2705,16 +2743,10 @@ paths: - oauth: - 'c-pat:read' - /poamLabels/available/{userId}: + /poamLabels: get: - summary: Return all poams with labels available to a user + summary: Return all poams containing labels that are available to the user operationId: getAvailablePoamLabels - parameters: - - in: path - name: userId - schema: - type: integer - required: true responses: '200': description: Poam array response @@ -3112,13 +3144,7 @@ paths: - oauth: - 'c-pat:read' - /poams/{userId}: - parameters: - - in: path - name: userId - required: true - schema: - type: integer + /poams: get: summary: Return all POAMs available to a user operationId: getAvailablePoams @@ -3512,8 +3538,10 @@ paths: security: - oauth: - 'c-pat:read' - summary: Update certain properties of a User + summary: Update properties of a User operationId: updateUser + parameters: + - $ref: '#/components/parameters/ElevateQuery' requestBody: required: true content: @@ -3556,377 +3584,46 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/SuccessResponse' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/error' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/error' - tags: - - User - security: - - oauth: - - 'c-pat:read' - - /user/updatePoints: - put: - summary: Updates a users points - operationId: updateUserPoints - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/UpdatePointsRequest' - responses: - '200': - description: Successfully updated the users points - content: - application/json: - schema: - $ref: '#/components/schemas/SuccessResponse' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/error' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/error' - tags: - - User - security: - - oauth: - - 'c-pat:read' - - '/users': - get: - tags: - - User - security: - - oauth: - - 'c-pat:op' - summary: Return all users for admin processing (assigning collections and permissions) - operationId: getUsers - responses: - '200': - description: User response - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/current_user_Response' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/error' - - '/user/{userId}': - parameters: - - in: path - name: userId - required: true - schema: - type: integer - get: - tags: - - User - security: - - oauth: - - 'c-pat:op' - summary: Return a User - operationId: getUserByUserID - responses: - '200': - description: User response - content: - application/json: - schema: - $ref: '#/components/schemas/user' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/error' - delete: - tags: - - User - security: - - oauth: - - 'c-pat:op' - summary: Delete a User - operationId: deleteUser - responses: - '200': - description: User response - content: - application/json: - schema: - $ref: '#/components/schemas/user' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/error' - - '/user/name/{userName}': - parameters: - - in: path - name: userName - required: true - schema: - type: string - get: - tags: - - User - security: - - oauth: - - 'c-pat:op' - summary: Return a User by userName - operationId: getUserByUserName - responses: - '200': - description: User response - content: - application/json: - schema: - $ref: '#/components/schemas/user' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/error' - - '/user/basic/{userId}': - parameters: - - in: path - name: userId - required: true - schema: - type: integer - get: - tags: - - User - security: - - oauth: - - 'c-pat:read' - summary: Return basic user information by userId - operationId: getBasicUserByUserID - responses: - '200': - description: Basic user response - content: - application/json: - schema: - $ref: '#/components/schemas/basic_user' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/error' - - '/user/loginState': - put: - tags: - - User - security: - - oauth: - - 'c-pat:read' - summary: Writes stdout log to indicate login or logout - operationId: loginState - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/user_loginState' - responses: - '201': - description: Login/Logout response - content: - application/json: - schema: - $ref: '#/components/schemas/user_Login_Response' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/error' - - '/vulnerability/poam/{vulnerabilityId}': - parameters: - - in: path - name: vulnerabilityId - required: true - schema: - type: string - get: - summary: Return POAMs by vulnerabilityId - operationId: getPoamsByVulnerabilityId - responses: - '200': - description: POAM array response - content: - application/json: - schema: - type: array - items: - $ref: '#/components/schemas/poam_Object' - '403': - $ref: '#/components/responses/forbidden' - '400': - description: Bad Request - content: - application/json: - schema: - $ref: '#/components/schemas/error' - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/error' - tags: - - Vulnerability - security: - - oauth: - - 'c-pat:read' - - '/vulnerability/existingPoams': - get: - summary: Return a list of vulnerabilities associated with existing POAMs - operationId: getExistingVulnerabilityPoams - responses: - '200': - description: Successful response with existing vulnerability POAMs - content: - application/json: - schema: - type: array - items: - type: object - properties: - poamId: - type: integer - vulnerabilityId: - type: string - '500': - description: Internal server error - content: - application/json: - schema: - $ref: '#/components/schemas/error' - tags: - - Vulnerability - security: - - oauth: - - 'c-pat:read' - - /op/configuration: - get: - summary: Return API version and classification - operationId: getConfiguration - responses: - '200': - description: Configuration response - content: - application/json: - schema: - $ref: '#/components/schemas/ApiConfiguration' - tags: - - Operation - security: [] - post: - summary: Set a configuration item - operationId: setConfigurationItem - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ConfigurationItem' - responses: - '200': - description: Configuration item updated successfully - content: - application/json: - schema: - $ref: '#/components/schemas/SuccessMessage' - '400': - description: Bad request - content: - application/json: - schema: - $ref: '#/components/schemas/error' - tags: - - Operation - security: - - oauth: - - 'c-pat:op' - delete: - summary: Delete a configuration item - operationId: deleteConfigurationItem - parameters: - - name: key - in: query - required: true - schema: - $ref: '#/components/schemas/String255' - responses: - '200': - description: Configuration item deleted successfully - content: - application/json: - schema: - $ref: '#/components/schemas/SuccessMessage' + $ref: '#/components/schemas/SuccessResponse' '400': description: Bad request content: application/json: schema: $ref: '#/components/schemas/error' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/error' tags: - - Operation + - User security: - oauth: - - 'c-pat:op' + - 'c-pat:read' - /vramUpdatedDate: - get: - summary: Get VRAM data updated date - description: Retrieves the last updated date of the VRAM data - operationId: getVramDataUpdatedDate + /user/updatePoints: + put: + summary: Updates a users points + operationId: updateUserPoints + parameters: + - $ref: '#/components/parameters/ElevateQuery' + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UpdatePointsRequest' responses: '200': - description: Successful response + description: Successfully updated the users points content: application/json: schema: - type: object - properties: - value: - type: string + $ref: '#/components/schemas/SuccessResponse' '400': - description: Bad Request + description: Bad request content: application/json: schema: @@ -3938,126 +3635,110 @@ paths: schema: $ref: '#/components/schemas/error' tags: - - IAV + - User security: - oauth: - - 'c-pat:op' - - /iavSummary: + - 'c-pat:read' + + '/users': get: - summary: Get IAV Summary - description: Retrieves IAV table data - operationId: getIAVTableData + tags: + - User + security: + - oauth: + - 'c-pat:op' + summary: Return all users for admin processing + operationId: getUsers + parameters: + - $ref: '#/components/parameters/ElevateQuery' responses: '200': - description: Successful response + description: User response content: application/json: schema: type: array items: - $ref: '#/components/schemas/IAVTableData' - '400': - description: Bad Request - content: - application/json: - schema: - $ref: '#/components/schemas/error' + $ref: '#/components/schemas/current_user_Response' '500': description: Internal server error content: application/json: schema: $ref: '#/components/schemas/error' + + '/user/{userId}': + parameters: + - $ref: '#/components/parameters/ElevateQuery' + - in: path + name: userId + required: true + schema: + type: integer + get: tags: - - IAV + - User security: - oauth: - - 'c-pat:read' - - /mapPluginIds: - post: - summary: Maps Plugin IDs to IAVs - description: Maps Plugin IDs to IAVs - operationId: mapIAVPluginIds - requestBody: - required: true - content: - application/json: - schema: - type: array - items: - type: object - properties: - iav: - type: string - pattern: '^\d{4}-[A-Z]-\d{4}$' - description: IAV in the format 0000-A-0000 - pluginID: - type: string - description: Comma-separated list of plugin IDs - required: - - iav - - pluginID + - 'c-pat:op' + summary: Return a User + operationId: getUserByUserID responses: '200': - description: Successful response + description: User response content: application/json: schema: - type: object - properties: - message: - type: string - updatedCount: - type: integer - '400': - description: Bad Request + $ref: '#/components/schemas/user' + '500': + description: Internal server error content: application/json: schema: $ref: '#/components/schemas/error' + delete: + tags: + - User + security: + - oauth: + - 'c-pat:op' + summary: Delete a User + operationId: deleteUser + responses: + '200': + description: User response + content: + application/json: + schema: + $ref: '#/components/schemas/user' '500': description: Internal server error content: application/json: schema: $ref: '#/components/schemas/error' - tags: - - IAV - security: - - oauth: - - 'c-pat:op' - /iav/pluginInfo: + '/vulnerability/poam/{vulnerabilityId}': + parameters: + - in: path + name: vulnerabilityId + required: true + schema: + type: string get: - summary: Get IAV and Navy Comply Date for Plugin IDs - description: Retrieves IAV and Navy Comply Date information for given Plugin IDs - operationId: getIAVInfoForPlugins - parameters: - - in: query - name: pluginIDs - required: true - schema: - type: string - description: Comma-separated list of Plugin IDs + summary: Return POAMs by vulnerabilityId + operationId: getPoamsByVulnerabilityId responses: '200': - description: Successful response + description: POAM array response content: application/json: schema: type: array items: - type: object - properties: - pluginID: - type: integer - iav: - type: string - navyComplyDate: - type: string - format: date + $ref: '#/components/schemas/poam_Object' + '403': + $ref: '#/components/responses/forbidden' '400': description: Bad Request content: @@ -4071,33 +3752,29 @@ paths: schema: $ref: '#/components/schemas/error' tags: - - IAV + - Vulnerability security: - oauth: - 'c-pat:read' - /iav/pluginIDs: + '/vulnerability/existingPoams': get: - summary: Retrieves mapped IAV pluginIDs - description: Get all unique pluginIDs that have an associated IAV as a comma-separated string - operationId: getIAVPluginIds + summary: Return a list of vulnerabilities associated with existing POAMs + operationId: getExistingVulnerabilityPoams responses: '200': - description: Successful response - content: - application/json: - schema: - type: object - properties: - pluginIDs: - type: string - description: Comma-separated list of unique plugin IDs - '400': - description: Bad Request + description: Successful response with existing vulnerability POAMs content: application/json: schema: - $ref: '#/components/schemas/error' + type: array + items: + type: object + properties: + poamId: + type: integer + vulnerabilityId: + type: string '500': description: Internal server error content: @@ -4105,10 +3782,10 @@ paths: schema: $ref: '#/components/schemas/error' tags: - - IAV + - Vulnerability security: - oauth: - - 'c-pat:read' + - 'c-pat:read' components: schemas: @@ -4656,6 +4333,29 @@ components: type: integer extensionJustification: type: string + mitigations: + type: string + requiredResources: + type: string + residualRisk: + type: string + likelihood: + type: string + relevanceOfThreat: + type: string + impactRating: + type: string + impactDescription: + type: string + status: + type: string + poamLog: + type: array + items: + type: object + properties: + userId: + type: integer poam_status_object: type: object @@ -4745,17 +4445,14 @@ components: relevanceOfThreat: type: string nullable: true - businessImpactRating: + impactRating: type: string nullable: true - businessImpactDescription: + impactDescription: type: string nullable: true status: type: string - vulnIdRestricted: - type: string - nullable: true submittedDate: type: string format: date @@ -5347,32 +5044,7 @@ components: type: integer nullable: true isAdmin: - type: integer - - user_Login: - required: - - email - type: object - additionalProperties: false - properties: - email: - type: string - - user_loginState: - required: - - loginState - type: object - additionalProperties: false - properties: - loginState: - type: string - - user_Login_Response: - type: object - additionalProperties: false - properties: - message: - type: string + type: integer user_Token: required: @@ -5507,6 +5179,14 @@ components: $ref: '#/components/schemas/error' parameters: + ElevateQuery: + name: elevate + in: query + description: Elevate the user context for this request if user is permitted (isAdmin) + schema: + type: boolean + default: false + collectionQuery: name: projection in: query @@ -5550,20 +5230,6 @@ components: description: A query parameter that identifies an Asset schema: type: string - userIdPath: - name: userId - in: path - description: A path parameter that identifies a User - required: true - schema: - type: string - collectionAuth: - name: userName - in: path - required: true - description: Returns collections belonging to a specif User - schema: - type: string securitySchemes: oauth: diff --git a/api/utils/auth.js b/api/utils/auth.js index 2f425807..b0a75c96 100644 --- a/api/utils/auth.js +++ b/api/utils/auth.js @@ -18,58 +18,42 @@ const { promisify } = require('util'); const User = require('../Services/usersService') const axios = require('axios'); const SmError = require('./error'); -const { format, isAfter } = require('date-fns'); +const { format, isAfter, differenceInMinutes } = require('date-fns'); -let jwksUri; -let client; +let jwksUri +let client const privilegeGetter = new Function("obj", "return obj?." + config.oauth.claims.privileges + " || [];"); const verifyRequest = async function (req, requiredScopes, securityDefinition) { - const token = getBearerToken(req); + + const token = getBearerToken(req) if (!token) { - throw new SmError.AuthorizeError("OIDC bearer token must be provided"); + throw (new SmError.AuthorizeError("OIDC bearer token must be provided")) } - - const options = { algorithms: ['RS256'] }; - const decoded = await verifyAndDecodeToken(token, getKey, options); - req.access_token = decoded; - req.bearer = token; - + const options = { + algorithms: ['RS256'] + } + const decoded = await verifyAndDecodeToken(token, getKey, options) + req.access_token = decoded + req.bearer = token req.userObject = { - userName: decoded[config.oauth.claims.username], - firstName: decoded[config.oauth.claims.firstname] || 'null', - lastName: decoded[config.oauth.claims.lastname] || 'null', - fullName: decoded[config.oauth.claims.fullname] || 'null', - email: decoded[config.oauth.claims.email], - lastClaims: req.access_token, - }; - - const usernamePrecedence = [config.oauth.claims.username, "preferred_username", config.oauth.claims.servicename, "azp", "client_id", "clientId"]; - req.userObject.userName = decoded[usernamePrecedence.find(element => !!decoded[element])]; - - if (req.userObject.userName === undefined) { - throw new SmError.PrivilegeError("No token claim mappable to username found"); + email: decoded[config.oauth.claims.email] || '', + firstName: decoded[config.oauth.claims.firstname] || '', + lastName: decoded[config.oauth.claims.lastname] || '', + fullName: decoded[config.oauth.claims.fullname] || '', } - let scopeClaim; - if (decoded[config.oauth.claims.scope]) { - scopeClaim = decoded[config.oauth.claims.scope]; - } else if (decoded.scope) { - scopeClaim = decoded.scope; - } else { - scopeClaim = null; + const usernamePrecedence = [config.oauth.claims.username, "preferred_username", config.oauth.claims.servicename, "azp", "client_id", "clientId"] + req.userObject.userName = decoded[usernamePrecedence.find(element => !!decoded[element])] + if (req.userObject.userName === undefined) { + throw (new SmError.PrivilegeError("No token claim mappable to username found")) } - let grantedScopes = []; - - if (typeof scopeClaim === 'string') { - grantedScopes = scopeClaim.split(' '); - } else if (Array.isArray(scopeClaim)) { - grantedScopes = scopeClaim; - } else { - logger.writeError('verifyRequest', 'invalidScopeType', { scopeClaim, grantedScopes }); - } + req.userObject.displayName = decoded[config.oauth.claims.name] || req.userObject.userName + const grantedScopes = typeof decoded[config.oauth.claims.scope] === 'string' ? + decoded[config.oauth.claims.scope].split(' ') : + decoded[config.oauth.claims.scope] const commonScopes = _.intersectionWith(grantedScopes, requiredScopes, function (gs, rs) { if (gs === rs) return gs let gsTokens = gs.split(":").filter(i => i.length) @@ -80,51 +64,49 @@ const verifyRequest = async function (req, requiredScopes, securityDefinition) { else { return gsTokens.every((t, i) => rsTokens[i] === t) } - }); - + }) if (commonScopes.length == 0) { throw (new SmError.PrivilegeError("Not in scope")) - } else { - const permissions = {}; - permissions.canAdmin = privilegeGetter(decoded).includes('admin'); - req.userObject.isAdmin = permissions.canAdmin ? 1 : 0; + } + else { + let isAdmin = privilegeGetter(decoded).includes('admin'); const response = await User.getUserByUserName(req.userObject.userName); - + if (response?.length > 1) req.userObject = response; + const adminValidation = response?.isAdmin === 1 ? isAdmin : 0; + req.userObject.isAdmin = adminValidation; req.userObject.userId = response?.userId || null; + const refreshFields = {} let now = new Date(); let formattedNow = format(now, 'yyyy-MM-dd HH:mm:ss'); - if (!response?.lastAccess) { - req.userObject.lastAccess = formattedNow; - } else { - let lastAccessDate = new Date(response.lastAccess); - if (isAfter(now, lastAccessDate)) { - req.userObject.lastAccess = formattedNow; - } else { - req.userObject.lastAccess = response.lastAccess; - } + if (!response?.lastAccess || differenceInMinutes(now, response?.lastAccess) >= config.settings.lastAccessResolution) { + refreshFields.lastAccess = now } if (!response?.lastClaims || decoded.jti !== response?.lastClaims?.jti) { - req.userObject.lastClaims = decoded - } else { - req.userObject.lastClaims = response.lastClaims + refreshFields.lastClaims = decoded } - if (req.userObject.userName) { - const userResponse = await User.setUserData(req.userObject); - const userId = userResponse.userId; - if (userId !== req.userObject.userId) { - req.userObject.userId = userId; - } + if (req.userObject.userName && (refreshFields.lastAccess || refreshFields.lastClaims)) { + if (req.userObject.userId === null) { + const userId = await User.setUserData(req.userObject, refreshFields, true); + if (userId.insertId != req.userObject.userId) { + req.userObject.userId = userId.insertId.toString(); + } + } else { + const userId = await User.setUserData(req.userObject, refreshFields, false); + if (userId.insertId != req.userObject.userId) { + req.userObject.userId = userId.insertId.toString(); + } + } } - - if ('elevate' in req.query && req.query.elevate === 'true' && !permissions.canAdmin) { - throw new SmError.PrivilegeError("User has insufficient privilege to complete this request."); + if ('elevate' in req.query && (req.query.elevate === 'true' && req.userObject.isAdmin != 1)) { + throw (new SmError.PrivilegeError("User has insufficient privilege to complete this request.")) } return true; } -}; + +} const verifyAndDecodeToken = promisify(jwt.verify); diff --git a/api/utils/config.js b/api/utils/config.js index 8bbc3eb2..18d7d975 100644 --- a/api/utils/config.js +++ b/api/utils/config.js @@ -21,6 +21,7 @@ let config = { }, settings: { setClassification: process.env.CPAT_CLASSIFICATION || "U", + lastAccessResolution: 60, responseValidation: process.env.CPAT_DEV_RESPONSE_VALIDATION || "none" }, client: { @@ -93,7 +94,7 @@ let config = { oauth: { authority: process.env.CPAT_OIDC_PROVIDER || "http://192.168.1.101:8080/auth/realms/RMFTools", claims: { - scope: process.env.CPAT_JWT_SCOPE_CLAIM || "c-pat:read c-pat:op openid profile offline_access", + scope: process.env.CPAT_JWT_SCOPE_CLAIM || "scope", username: process.env.CPAT_JWT_USERNAME_CLAIM || "preferred_username", servicename: process.env.CPAT_JWT_SERVICENAME_CLAIM || "clientId", fullname: process.env.CPAT_JWT_FULL_NAME_CLAIM || process.env.CPAT_JWT_USERNAME_CLAIM || "name", diff --git a/client/package-lock.json b/client/package-lock.json index cd482440..aa180f27 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -8,33 +8,33 @@ "name": "cpat-frontend", "version": "0.3.0", "dependencies": { - "@angular/animations": "^18.2.0", - "@angular/cdk": "^18.2.0", - "@angular/common": "^18.2.0", - "@angular/compiler": "^18.2.0", - "@angular/core": "^18.2.0", - "@angular/forms": "^18.2.0", - "@angular/localize": "^18.2.0", - "@angular/material": "^18.2.0", - "@angular/platform-browser": "^18.2.0", - "@angular/platform-browser-dynamic": "^18.2.0", - "@angular/router": "^18.2.0", + "@angular/animations": "^18.2.1", + "@angular/cdk": "^18.2.1", + "@angular/common": "^18.2.1", + "@angular/compiler": "^18.2.1", + "@angular/core": "^18.2.1", + "@angular/forms": "^18.2.1", + "@angular/localize": "^18.2.1", + "@angular/material": "^18.2.1", + "@angular/platform-browser": "^18.2.1", + "@angular/platform-browser-dynamic": "^18.2.1", + "@angular/router": "^18.2.1", "@fortawesome/fontawesome-free": "^6.6.0", "@ng-bootstrap/ng-bootstrap": "^17.0.0", "@swimlane/ngx-charts": "^20.5.0", "@tsparticles/angular": "^3.0.0", "angular-auth-oidc-client": "^18.0.1", "bootstrap": "^5.3.3", - "chart.js": "^4.4.3", + "chart.js": "^4.4.4", "chartjs-plugin-datalabels": "^2.2.0", "classlist.js": "^1.1.20150312", "date-fns": "^3.6.0", "exceljs": "^4.4.0", - "instantsearch.js": "^4.73.4", + "instantsearch.js": "^4.74.0", "json-to-plain-text": "^1.1.4", "jsonwebtoken": "^9.0.2", "ng2-charts": "^6.0.1", - "npm-check-updates": "^17.0.6", + "npm-check-updates": "^17.1.0", "pace-js": "^1.2.4", "primeflex": "^3.3.1", "primeicons": "^7.0.0", @@ -43,105 +43,88 @@ "subsink": "^1.0.2", "typescript": "^5.5.4", "web-animations-js": "^2.3.2", - "zone.js": "^0.14.10" + "zone.js": "^0.15.0" }, "devDependencies": { - "@angular-devkit/build-angular": "^18.2.0", - "@angular/cli": "^18.2.0", - "@angular/compiler-cli": "^18.2.0", + "@angular-devkit/build-angular": "^18.2.1", + "@angular/cli": "^18.2.1", + "@angular/compiler-cli": "^18.2.1", "@types/chart.js": "^2.9.41", "@types/d3": "^7.4.3", "@types/jasmine": "^5.1.4", "@types/jquery": "^3.5.30", - "@types/node": "^22.4.1", + "@types/node": "^22.5.0", "jasmine-core": "^5.2.0" } }, - "node_modules/@algolia/cache-browser-local-storage": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.24.0.tgz", - "integrity": "sha512-t63W9BnoXVrGy9iYHBgObNXqYXM3tYXCjDSHeNwnsc324r4o5UiVKUiAB4THQ5z9U5hTj6qUvwg/Ez43ZD85ww==", - "license": "MIT", - "peer": true, - "dependencies": { - "@algolia/cache-common": "4.24.0" - } - }, - "node_modules/@algolia/cache-common": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.24.0.tgz", - "integrity": "sha512-emi+v+DmVLpMGhp0V9q9h5CdkURsNmFC+cOS6uK9ndeJm9J4TiqSvPYVu+THUP8P/S08rxf5x2P+p3CfID0Y4g==", - "license": "MIT", - "peer": true - }, - "node_modules/@algolia/cache-in-memory": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.24.0.tgz", - "integrity": "sha512-gDrt2so19jW26jY3/MkFg5mEypFIPbPoXsQGQWAi6TrCPsNOSEYepBMPlucqWigsmEy/prp5ug2jy/N3PVG/8w==", - "license": "MIT", - "peer": true, - "dependencies": { - "@algolia/cache-common": "4.24.0" - } - }, - "node_modules/@algolia/client-account": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.24.0.tgz", - "integrity": "sha512-adcvyJ3KjPZFDybxlqnf+5KgxJtBjwTPTeyG2aOyoJvx0Y8dUQAEOEVOJ/GBxX0WWNbmaSrhDURMhc+QeevDsA==", + "node_modules/@algolia/client-abtesting": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.1.0.tgz", + "integrity": "sha512-WKL6ryH+CYCUlDz1Sr4M/SUn5qoRyqmGbKNen5PoFtEA9GWuM6lFk9XvsKvh0UwH/k20tBKq75NTOCUEA5gVRw==", "license": "MIT", "peer": true, "dependencies": { - "@algolia/client-common": "4.24.0", - "@algolia/client-search": "4.24.0", - "@algolia/transporter": "4.24.0" + "@algolia/client-common": "5.1.0", + "@algolia/requester-browser-xhr": "5.1.0", + "@algolia/requester-node-http": "5.1.0" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@algolia/client-analytics": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.24.0.tgz", - "integrity": "sha512-y8jOZt1OjwWU4N2qr8G4AxXAzaa8DBvyHTWlHzX/7Me1LX8OayfgHexqrsL4vSBcoMmVw2XnVW9MhL+Y2ZDJXg==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.1.0.tgz", + "integrity": "sha512-HqJa0z+8maRmsNTxdytyr4ADt735fEK/0S/doKuRqYgloGanIB1njxuq2wP8oW1ARfo5mlCJ7dF1csO+/V/fVQ==", "license": "MIT", "peer": true, "dependencies": { - "@algolia/client-common": "4.24.0", - "@algolia/client-search": "4.24.0", - "@algolia/requester-common": "4.24.0", - "@algolia/transporter": "4.24.0" + "@algolia/client-common": "5.1.0", + "@algolia/requester-browser-xhr": "5.1.0", + "@algolia/requester-node-http": "5.1.0" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@algolia/client-common": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", - "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.1.0.tgz", + "integrity": "sha512-/PUtnEKpI9opioNfVKA5cBGQr0yulE3OWJCf9xWjJRFn+CvTAWkIecBWF9R+GpxeoaTqf3lWqDwYt8zEBXU11Q==", "license": "MIT", "peer": true, - "dependencies": { - "@algolia/requester-common": "4.24.0", - "@algolia/transporter": "4.24.0" + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@algolia/client-personalization": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.24.0.tgz", - "integrity": "sha512-l5FRFm/yngztweU0HdUzz1rC4yoWCFo3IF+dVIVTfEPg906eZg5BOd1k0K6rZx5JzyyoP4LdmOikfkfGsKVE9w==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.1.0.tgz", + "integrity": "sha512-OkUJmksAJK5Q0WAwX+QTfN84zsohD08KIljO0+IZYagm0LoPHF6corYYkF1hV1wcOmFwpjla+cIV+h8TcA59fQ==", "license": "MIT", "peer": true, "dependencies": { - "@algolia/client-common": "4.24.0", - "@algolia/requester-common": "4.24.0", - "@algolia/transporter": "4.24.0" + "@algolia/client-common": "5.1.0", + "@algolia/requester-browser-xhr": "5.1.0", + "@algolia/requester-node-http": "5.1.0" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@algolia/client-search": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", - "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.1.0.tgz", + "integrity": "sha512-zPMO+iX+PnrQAX2tzW7ViletvkDNqLG1CI0k+9wXZtyVuQrPI6pxho/mANlC9QHmiMH3eR2KibQCOBMifOd61w==", "license": "MIT", "peer": true, "dependencies": { - "@algolia/client-common": "4.24.0", - "@algolia/requester-common": "4.24.0", - "@algolia/transporter": "4.24.0" + "@algolia/client-common": "5.1.0", + "@algolia/requester-browser-xhr": "5.1.0", + "@algolia/requester-node-http": "5.1.0" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@algolia/events": { @@ -150,80 +133,45 @@ "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==", "license": "MIT" }, - "node_modules/@algolia/logger-common": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.24.0.tgz", - "integrity": "sha512-LLUNjkahj9KtKYrQhFKCzMx0BY3RnNP4FEtO+sBybCjJ73E8jNdaKJ/Dd8A/VA4imVHP5tADZ8pn5B8Ga/wTMA==", - "license": "MIT", - "peer": true - }, - "node_modules/@algolia/logger-console": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.24.0.tgz", - "integrity": "sha512-X4C8IoHgHfiUROfoRCV+lzSy+LHMgkoEEU1BbKcsfnV0i0S20zyy0NLww9dwVHUWNfPPxdMU+/wKmLGYf96yTg==", - "license": "MIT", - "peer": true, - "dependencies": { - "@algolia/logger-common": "4.24.0" - } - }, "node_modules/@algolia/recommend": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-4.24.0.tgz", - "integrity": "sha512-P9kcgerfVBpfYHDfVZDvvdJv0lEoCvzNlOy2nykyt5bK8TyieYyiD0lguIJdRZZYGre03WIAFf14pgE+V+IBlw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.1.0.tgz", + "integrity": "sha512-0pF1DcB/1fNCOU6DXcI3v1h/sADT9ueACoLcYZd5YUYl/X3wsq/BBPRi/NY1r93hi+LyTfIbywS5pgZ3v4fRDg==", "license": "MIT", "peer": true, "dependencies": { - "@algolia/cache-browser-local-storage": "4.24.0", - "@algolia/cache-common": "4.24.0", - "@algolia/cache-in-memory": "4.24.0", - "@algolia/client-common": "4.24.0", - "@algolia/client-search": "4.24.0", - "@algolia/logger-common": "4.24.0", - "@algolia/logger-console": "4.24.0", - "@algolia/requester-browser-xhr": "4.24.0", - "@algolia/requester-common": "4.24.0", - "@algolia/requester-node-http": "4.24.0", - "@algolia/transporter": "4.24.0" + "@algolia/client-common": "5.1.0", + "@algolia/requester-browser-xhr": "5.1.0", + "@algolia/requester-node-http": "5.1.0" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.24.0.tgz", - "integrity": "sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.1.0.tgz", + "integrity": "sha512-q2HpJBxVnvmk9UfphzjGgYSKDX/qXeuPmKlybYAX1dZ1/b8LBOt066JjS5L9CXuxW26Igi/PrdbzA9tL8jD3QQ==", "license": "MIT", "peer": true, "dependencies": { - "@algolia/requester-common": "4.24.0" + "@algolia/client-common": "5.1.0" + }, + "engines": { + "node": ">= 14.0.0" } }, - "node_modules/@algolia/requester-common": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.24.0.tgz", - "integrity": "sha512-k3CXJ2OVnvgE3HMwcojpvY6d9kgKMPRxs/kVohrwF5WMr2fnqojnycZkxPoEg+bXm8fi5BBfFmOqgYztRtHsQA==", - "license": "MIT", - "peer": true - }, "node_modules/@algolia/requester-node-http": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.24.0.tgz", - "integrity": "sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@algolia/requester-common": "4.24.0" - } - }, - "node_modules/@algolia/transporter": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.24.0.tgz", - "integrity": "sha512-86nI7w6NzWxd1Zp9q3413dRshDqAzSbsQjhcDhPIatEFiZrL1/TjnHL8S7jVKFePlIMzDsZWXAXwXzcok9c5oA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.1.0.tgz", + "integrity": "sha512-P51ZDjoswq6OLVY+vES+EekkC0OPABKrJ9s4S1UKNTvggDIn38BoM9L25K7TEWEmjCG68EKDGSPUAt1q/dS0Uw==", "license": "MIT", "peer": true, "dependencies": { - "@algolia/cache-common": "4.24.0", - "@algolia/logger-common": "4.24.0", - "@algolia/requester-common": "4.24.0" + "@algolia/client-common": "5.1.0" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@ampproject/remapping": { @@ -240,13 +188,13 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1802.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.0.tgz", - "integrity": "sha512-s1atTSL98XLUUxfWEQAnhFaOFIJZDLWjSqec+Sb+f4iZFj+hOFejzJxPVnHMIJudOzn0Lqjk3t987KG/zNEGdg==", + "version": "0.1802.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1802.1.tgz", + "integrity": "sha512-XTnJfCBMDQl3xF4w/eNrq821gbj2Ig1cqbzpRflhz4pqrANTAfHfPoIC7piWEZ60FNlHapzb6fvh6tJUGXG9og==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.0", + "@angular-devkit/core": "18.2.1", "rxjs": "7.8.1" }, "engines": { @@ -256,17 +204,17 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.0.tgz", - "integrity": "sha512-V0XKT7xt6d6duXqmDAQEQgEJNXuWAekpHEDxafvBT0MTxcEhu0ozQVwI4oAekiKOz+APIcAZyMzvw3Tlzog5cw==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-18.2.1.tgz", + "integrity": "sha512-ANsTWKjIlEvJ6s276TbwnDhkoHhQDfsNiRFUDRGBZu94UNR78ImQZSyKYGHJOeQQH6jpBtraA1rvW5WKozAtlw==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1802.0", - "@angular-devkit/build-webpack": "0.1802.0", - "@angular-devkit/core": "18.2.0", - "@angular/build": "18.2.0", + "@angular-devkit/architect": "0.1802.1", + "@angular-devkit/build-webpack": "0.1802.1", + "@angular-devkit/core": "18.2.1", + "@angular/build": "18.2.1", "@babel/core": "7.25.2", "@babel/generator": "7.25.0", "@babel/helper-annotate-as-pure": "7.24.7", @@ -277,7 +225,7 @@ "@babel/preset-env": "7.25.3", "@babel/runtime": "7.25.0", "@discoveryjs/json-ext": "0.6.1", - "@ngtools/webpack": "18.2.0", + "@ngtools/webpack": "18.2.1", "@vitejs/plugin-basic-ssl": "1.1.0", "ansi-colors": "4.1.3", "autoprefixer": "10.4.20", @@ -309,7 +257,7 @@ "postcss-loader": "8.1.1", "resolve-url-loader": "5.0.0", "rxjs": "7.8.1", - "sass": "1.77.8", + "sass": "1.77.6", "sass-loader": "16.0.0", "semver": "7.6.3", "source-map-loader": "5.0.0", @@ -384,27 +332,30 @@ } } }, - "node_modules/@angular-devkit/build-angular/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "node_modules/@angular-devkit/build-angular/node_modules/@babel/generator": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", + "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" }, "engines": { - "node": ">=10" + "node": ">=6.9.0" } }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1802.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.0.tgz", - "integrity": "sha512-bU7AxlI/avnlOLrgE195cokrOA1ayT6JjRv8Hxzh1bIOa1jE87HsyjxvQhOLcdEb97zwHqMqbntcgUNBgsegJQ==", + "version": "0.1802.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1802.1.tgz", + "integrity": "sha512-xOP9Hxkj/mWYdMTa/8uNxFTv7z+3UiGdt4VAO7vetV5qkU/S9rRq8FEKviCc2llXfwkhInSgeeHpWKdATa+YIQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1802.0", + "@angular-devkit/architect": "0.1802.1", "rxjs": "7.8.1" }, "engines": { @@ -418,9 +369,9 @@ } }, "node_modules/@angular-devkit/core": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.0.tgz", - "integrity": "sha512-8SOopyUKUMqAq2rj32XkTIfr79Y274k4uglxxRtzHYoWwHlLdG0KrA+wCcsh/FU9PyR4dA+5dcDAApn358ZF+Q==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-18.2.1.tgz", + "integrity": "sha512-fSuGj6CxiTFR+yjuVcaWqaVb5Wts39CSBYRO1BlsOlbuWFZ2NKC/BAb5bdxpB31heCBJi7e3XbPvcMMJIcnKlA==", "dev": true, "license": "MIT", "dependencies": { @@ -446,13 +397,13 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.0.tgz", - "integrity": "sha512-WWKwz2RKMVI6T25JFwOSSfRLB+anNzabVIRwf85R/YMIo34BUk777f2JU/7cCKlxSpQu39TqIfMQZAyzAD1z0A==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-18.2.1.tgz", + "integrity": "sha512-2t/q0Jcv7yqhAzEdNgsxoGSCmPgD4qfnVOJ7EJw3LNIA+kX1CmtN4FESUS0i49kN4AyNJFAI5O2pV8iJiliKaw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.0", + "@angular-devkit/core": "18.2.1", "jsonc-parser": "3.3.1", "magic-string": "0.30.11", "ora": "5.4.1", @@ -465,9 +416,9 @@ } }, "node_modules/@angular/animations": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.0.tgz", - "integrity": "sha512-BFAfqDDjsa0Q91F4s33pFcBG+ydFDurEmwYGG1gmO7UXZJI6ZbRVdULaZHz75M+Bf3hJkzVB05pesvfbK+Fg/g==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-18.2.1.tgz", + "integrity": "sha512-jit452yuE6DMVV09E6RAjgapgw64mMVH31ccpPvMDekzPsTuP3KNKtgRFU/k2DFhYJvyczM1AqqlgccE/JGaRw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -476,18 +427,18 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.2.0" + "@angular/core": "18.2.1" } }, "node_modules/@angular/build": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.0.tgz", - "integrity": "sha512-LvNJ2VOEVy3N1tGzt+xnKyweRBuUE1NsnuEEWAb9Y+V1cyRgj0s7FX2+IQZZQhP+W/pXfbsKaByOAbJ5KWV85Q==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-18.2.1.tgz", + "integrity": "sha512-HwzjB+I31cAtjTTbbS2NbayzfcWthaKaofJlSmZIst3PN+GwLZ8DU0DRpd/xu5AXkk+DoAIWd+lzUIaqngz6ow==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1802.0", + "@angular-devkit/architect": "0.1802.1", "@babel/core": "7.25.2", "@babel/helper-annotate-as-pure": "7.24.7", "@babel/helper-split-export-declaration": "7.24.7", @@ -507,7 +458,7 @@ "picomatch": "4.0.2", "piscina": "4.6.1", "rollup": "4.20.0", - "sass": "1.77.8", + "sass": "1.77.6", "semver": "7.6.3", "vite": "5.4.0", "watchpack": "2.4.1" @@ -548,23 +499,10 @@ } } }, - "node_modules/@angular/build/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@angular/cdk": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.0.tgz", - "integrity": "sha512-hjuUWNhxU48WB2i1j4NLwnPTaCnucRElfp7DBX5Io0rY5Lwl3HXafvyi7/A1D0Ah+YsJpktKOWPDGv8r7vxymg==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.1.tgz", + "integrity": "sha512-6y4MmpEPXze6igUHkLsBUPkxw32F8+rmW0xVXZchkSyGlFgqfh53ueXoryWb0qL4s5enkNY6AzXnKAqHfPNkVQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -579,18 +517,18 @@ } }, "node_modules/@angular/cli": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.0.tgz", - "integrity": "sha512-hA60QIA7Dh8LQxM42wqd7WrhwQjbjB8oIRH5Slgbiv8iocAo76scp1/qyZo2SSzjfkB7jxUiao5L4ckiJ/hvZg==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.2.1.tgz", + "integrity": "sha512-SomUFDHanY4o7k3XBGf1eFt4z1h05IGJHfcbl2vxoc0lY59VN13m/pZsD2AtpqtJTzLQT02XQOUP4rmBbGoQ+Q==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1802.0", - "@angular-devkit/core": "18.2.0", - "@angular-devkit/schematics": "18.2.0", + "@angular-devkit/architect": "0.1802.1", + "@angular-devkit/core": "18.2.1", + "@angular-devkit/schematics": "18.2.1", "@inquirer/prompts": "5.3.8", "@listr2/prompt-adapter-inquirer": "2.0.15", - "@schematics/angular": "18.2.0", + "@schematics/angular": "18.2.1", "@yarnpkg/lockfile": "1.1.0", "ini": "4.1.3", "jsonc-parser": "3.3.1", @@ -612,23 +550,10 @@ "yarn": ">= 1.13.0" } }, - "node_modules/@angular/cli/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@angular/common": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.0.tgz", - "integrity": "sha512-DELx/QYNqqjmiM+kE5PoVmyG4gPw5WB1bDDeg3hEuBCK3eS2KosgQH0/MQo3OSBZHOcAMFjfHMGqKgxndmYixQ==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-18.2.1.tgz", + "integrity": "sha512-N0ZJO1/iU9UhprplZRPvBcdRgA/i6l6Ng5gXs5ymHBJ0lxsB+mDVCmC4jISjR9gAWc426xXwLaOpuP5Gv3f/yg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -637,14 +562,14 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.2.0", + "@angular/core": "18.2.1", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.0.tgz", - "integrity": "sha512-RmGwQ7jRzotUKKMk0CwxTcIXFr5mRxSbJf9o5S3ujuIOo1lYop8SQZvjq67a5BuoYyD+1pX6iMmxZqlbKoihBQ==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-18.2.1.tgz", + "integrity": "sha512-5e9ygKEcsBoV6xpaGKVrtsLxLETlrM0oB7twl4qG/xuKYqCLj8cRQMcAKSqDfTPzWMOAQc7pHdk+uFVo/8dWHA==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -653,7 +578,7 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/core": "18.2.0" + "@angular/core": "18.2.1" }, "peerDependenciesMeta": { "@angular/core": { @@ -662,9 +587,9 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.0.tgz", - "integrity": "sha512-pPBFjMqNTNettrleLtEc6a/ysOZjG3F0Z5lyKYePcyNQmn2rpa9XmD2y6PhmzTmIhxeXrogWA84Xgg/vK5wBNw==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-18.2.1.tgz", + "integrity": "sha512-D+Qba0r6RfHfffzrebGYp54h05AxpkagLjit/GczKNgWSP1gIgZxSfi88D+GvFmeWvZxWN1ecAQ+yqft9hJqWg==", "license": "MIT", "dependencies": { "@babel/core": "7.25.2", @@ -685,14 +610,14 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "18.2.0", + "@angular/compiler": "18.2.1", "typescript": ">=5.4 <5.6" } }, "node_modules/@angular/core": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.0.tgz", - "integrity": "sha512-7+4wXfeAk1TnG3MGho2gpBI5XHxeSRWzLK2rC5qyyRbmMV+GrIgf1HqFjQ4S02rydkQvGpjqQHtO1PYJnyn4bg==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-18.2.1.tgz", + "integrity": "sha512-9KrSpJ65UlJZNXrE18NszcfOwb5LZgG+LYi5Doe7amt218R1bzb3trvuAm0ZzMaoKh4ugtUCkzEOd4FALPEX6w==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -706,9 +631,9 @@ } }, "node_modules/@angular/forms": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.0.tgz", - "integrity": "sha512-G+4BjNCUo4cRwg9NwisGLbtG/1AbIJNOO3RUejPJJbCcAkYMSzmDWSQ+LQEGW4vC/1xaDKO8AT71DI/I09bOxA==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-18.2.1.tgz", + "integrity": "sha512-T7z8KUuj2PoPxrMrAruQVJha+x4a9Y6IrKYtArgOQQlTwCEJuqpVYuOk5l3fwWpHE9bVEjvgkAMI1D5YXA/U6w==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -717,16 +642,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.0", - "@angular/core": "18.2.0", - "@angular/platform-browser": "18.2.0", + "@angular/common": "18.2.1", + "@angular/core": "18.2.1", + "@angular/platform-browser": "18.2.1", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/localize": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-18.2.0.tgz", - "integrity": "sha512-ul8yGmimiHkhUU87isDCst0790jTBHP1zPBMI2q7QHv7iDzSN5brV8zUMcRypxhh4mQOCnq2LtP84o5ybn4Sig==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-18.2.1.tgz", + "integrity": "sha512-nNdB6ehXCSBpQ75sTh6Gcwy2rgExfZEkGcPARJLpjqQlHO+Mk3b1y3ka6XT9M2qQYUeyukncTFUMEZWwHICsOA==", "license": "MIT", "dependencies": { "@babel/core": "7.25.2", @@ -743,21 +668,21 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/compiler": "18.2.0", - "@angular/compiler-cli": "18.2.0" + "@angular/compiler": "18.2.1", + "@angular/compiler-cli": "18.2.1" } }, "node_modules/@angular/material": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-18.2.0.tgz", - "integrity": "sha512-lOXk8pAVP4Mr0/Q6YrNnVYQVTnR8aEG5D9QSEWjs+607gONuh/9n7ERBdzX7xO9D0vYyXq+Vil32zcF41/Q8Cg==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-18.2.1.tgz", + "integrity": "sha512-DBSJGqLttT9vYpLGWTuuRoOKd1mNelS0jnNo7jNZyMpjcGfuhNzmPtYiBkXfNsAl7YoXoUmX8+4uh1JZspQGqA==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { "@angular/animations": "^18.0.0 || ^19.0.0", - "@angular/cdk": "18.2.0", + "@angular/cdk": "18.2.1", "@angular/common": "^18.0.0 || ^19.0.0", "@angular/core": "^18.0.0 || ^19.0.0", "@angular/forms": "^18.0.0 || ^19.0.0", @@ -766,9 +691,9 @@ } }, "node_modules/@angular/platform-browser": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.0.tgz", - "integrity": "sha512-yhj281TuPz5a8CehwucwIVl29Oqte9KS4X/VQkMV++GpYQE2KKKcoff4FXSdF5RUcUYkK2li4IvawIqPmUSagg==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-18.2.1.tgz", + "integrity": "sha512-hQABX7QotGmCIR3EhCBCDh5ZTvQao+JkuK5CCw2G1PkRfJMBwEpjNqnyhz41hZhWiGlucp9jgbeypppW+mIQEw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -777,9 +702,9 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/animations": "18.2.0", - "@angular/common": "18.2.0", - "@angular/core": "18.2.0" + "@angular/animations": "18.2.1", + "@angular/common": "18.2.1", + "@angular/core": "18.2.1" }, "peerDependenciesMeta": { "@angular/animations": { @@ -788,9 +713,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.0.tgz", - "integrity": "sha512-izfaXKNC/kqOEzJG8eTnFu39VLI3vv3dTKoYOdEKRB7MTI0t0x66oZmABnHcihtkTSvXs/is+7lA5HmH2ZuIEQ==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-18.2.1.tgz", + "integrity": "sha512-tYJHtshbaKrtnRA15k3vrveSVBqkVUGhINvGugFA2vMtdTOfhfPw+hhzYrcwJibgU49rHogCfI9mkIbpNRYntA==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -799,16 +724,16 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.0", - "@angular/compiler": "18.2.0", - "@angular/core": "18.2.0", - "@angular/platform-browser": "18.2.0" + "@angular/common": "18.2.1", + "@angular/compiler": "18.2.1", + "@angular/core": "18.2.1", + "@angular/platform-browser": "18.2.1" } }, "node_modules/@angular/router": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.0.tgz", - "integrity": "sha512-6/462hvC3HSwbps8VItECHpkdkiFqRmTKdn1Trik+FjnvdupYrKB6kBsveM3eP+gZD4zyMBMKzBWB9N/xA1clw==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-18.2.1.tgz", + "integrity": "sha512-gVyqW6fYnG7oq1DlZSXJMQ2Py2dJQB7g6XVtRcYB1gR4aeowx5N9ws7PjqAi0ih91ASq2MmP4OlSSWLq+eaMGg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -817,9 +742,9 @@ "node": "^18.19.1 || ^20.11.1 || >=22.0.0" }, "peerDependencies": { - "@angular/common": "18.2.0", - "@angular/core": "18.2.0", - "@angular/platform-browser": "18.2.0", + "@angular/common": "18.2.1", + "@angular/core": "18.2.1", + "@angular/platform-browser": "18.2.1", "rxjs": "^6.5.3 || ^7.4.0" } }, @@ -891,12 +816,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.0.tgz", - "integrity": "sha512-3LEEcj3PVW8pW2R1SR1M89g/qrYk/m/mB/tLqn7dn4sbBUQyTqnlod+II2U4dqiGtUmkcnAmkMDralTFZttRiw==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.4.tgz", + "integrity": "sha512-NFtZmZsyzDPJnk9Zg3BbTfKKc9UlHYzD0E//p2Z3B9nCwwtJW9T0gVbCz8+fBngnn4zf1Dr3IK8PHQQHq0lDQw==", "license": "MIT", "dependencies": { - "@babel/types": "^7.25.0", + "@babel/types": "^7.25.4", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^2.5.1" @@ -958,9 +883,9 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.0.tgz", - "integrity": "sha512-GYM6BxeQsETc9mnct+nIIpf63SAyzvyYN7UB/IlTyd+MBg06afFGp0mIeUqGyWgS2mxad6vqbMrHVlaL3m70sQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.4.tgz", + "integrity": "sha512-ro/bFs3/84MDgDmMwbcHgDa8/E6J3QKNTk4xJJnVeFtGE+tL0K26E3pNxhYz2b67fJpt7Aphw5XcploKXuCvCQ==", "dev": true, "license": "MIT", "dependencies": { @@ -969,7 +894,7 @@ "@babel/helper-optimise-call-expression": "^7.24.7", "@babel/helper-replace-supers": "^7.25.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/traverse": "^7.25.0", + "@babel/traverse": "^7.25.4", "semver": "^6.3.1" }, "engines": { @@ -1249,12 +1174,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", - "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.4.tgz", + "integrity": "sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==", "license": "MIT", "dependencies": { - "@babel/types": "^7.25.2" + "@babel/types": "^7.25.4" }, "bin": { "parser": "bin/babel-parser.js" @@ -1699,14 +1624,14 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.7.tgz", - "integrity": "sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.4.tgz", + "integrity": "sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-class-features-plugin": "^7.25.4", + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1734,17 +1659,17 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.0.tgz", - "integrity": "sha512-xyi6qjr/fYU304fiRwFbekzkqVJZ6A7hOjWZd+89FVcBqPV3S9Wuozz82xdpLspckeaafntbzglaW4pqpzvtSw==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.4.tgz", + "integrity": "sha512-oexUfaQle2pF/b6E0dwsxQtAol9TLSO88kQvym6HHBWFliV2lGdrPieX+WgMRLSJDVzdYywk7jXbLPuO2KLTLg==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-compilation-targets": "^7.25.2", "@babel/helper-plugin-utils": "^7.24.8", "@babel/helper-replace-supers": "^7.25.0", - "@babel/traverse": "^7.25.0", + "@babel/traverse": "^7.25.4", "globals": "^11.1.0" }, "engines": { @@ -2215,14 +2140,14 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz", - "integrity": "sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.4.tgz", + "integrity": "sha512-ao8BG7E2b/URaUQGqN3Tlsg+M3KlHY6rJ1O1gXAEUnZoyNQnvKyH87Kfg+FoxSeyWUB8ISZZsC91C44ZuBFytw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-class-features-plugin": "^7.25.4", + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -2462,14 +2387,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.7.tgz", - "integrity": "sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.4.tgz", + "integrity": "sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.2", + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -2635,16 +2560,16 @@ } }, "node_modules/@babel/traverse": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.3.tgz", - "integrity": "sha512-HefgyP1x754oGCsKmV5reSmtV7IXj/kpaE1XYY+D9G5PvKKoFfSbiS4M77MdjuwlZKDIKFCffq9rPU+H/s3ZdQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.4.tgz", + "integrity": "sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.25.0", - "@babel/parser": "^7.25.3", + "@babel/generator": "^7.25.4", + "@babel/parser": "^7.25.4", "@babel/template": "^7.25.0", - "@babel/types": "^7.25.2", + "@babel/types": "^7.25.4", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2653,9 +2578,9 @@ } }, "node_modules/@babel/types": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", - "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz", + "integrity": "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.24.8", @@ -3800,9 +3725,9 @@ } }, "node_modules/@ngtools/webpack": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.0.tgz", - "integrity": "sha512-a6hbkYzh/KUlI52huiU4vztqIuxzyddg6kJGcelUJx3Ju6MJeziu+XmJ6wqFRvfH89zmJeaSADKsGFQaBHtJLg==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-18.2.1.tgz", + "integrity": "sha512-v86U3jOoy5R9ZWe9Q0LbHRx/IBw1lbn0ldBU+gIIepREyVvb9CcH/vAyIb2Fw1zaYvvfG1OyzdrHyW8iGXjdnQ==", "dev": true, "license": "MIT", "engines": { @@ -4297,14 +4222,14 @@ ] }, "node_modules/@schematics/angular": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.0.tgz", - "integrity": "sha512-XePvx2ZnxCcAQw5lHVMUrJvm8MXqAWGcMyJDAuQUqNZrPCk3GpCaplWx2n+nPkinYVX2Q2v/DqtvWStQwgU4nA==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-18.2.1.tgz", + "integrity": "sha512-bBV7I+MCbdQmBPUFF4ECg37VReM0+AdQsxgwkjBBSYExmkErkDoDgKquwL/tH7stDCc5IfTd0g9BMeosRgDMug==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "18.2.0", - "@angular-devkit/schematics": "18.2.0", + "@angular-devkit/core": "18.2.1", + "@angular-devkit/schematics": "18.2.1", "jsonc-parser": "3.3.1" }, "engines": { @@ -5056,9 +4981,9 @@ } }, "node_modules/@types/node": { - "version": "22.4.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.4.1.tgz", - "integrity": "sha512-1tbpb9325+gPnKK0dMm+/LMriX0vKxf6RnB0SZUqfyVkQ4fMgUSySqhxE/y8Jvs4NyF1yHzTfG9KlnkIODxPKg==", + "version": "22.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.0.tgz", + "integrity": "sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==", "dev": true, "license": "MIT", "dependencies": { @@ -5505,33 +5430,29 @@ } }, "node_modules/algoliasearch": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.24.0.tgz", - "integrity": "sha512-bf0QV/9jVejssFBmz2HQLxUadxk574t4iwjCKp5E7NBzwKkrDEhKPISIIjAU/p6K5qDx3qoeh4+26zWN1jmw3g==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.1.0.tgz", + "integrity": "sha512-fN+g3ebMNA3+pCWC9P2Jbw6m3EBaEaQViXkq/j3XCJUJ0j1+aUsic0CmsBja4wdci5Eovv8dL8VTcURmvq9Fyg==", "license": "MIT", "peer": true, "dependencies": { - "@algolia/cache-browser-local-storage": "4.24.0", - "@algolia/cache-common": "4.24.0", - "@algolia/cache-in-memory": "4.24.0", - "@algolia/client-account": "4.24.0", - "@algolia/client-analytics": "4.24.0", - "@algolia/client-common": "4.24.0", - "@algolia/client-personalization": "4.24.0", - "@algolia/client-search": "4.24.0", - "@algolia/logger-common": "4.24.0", - "@algolia/logger-console": "4.24.0", - "@algolia/recommend": "4.24.0", - "@algolia/requester-browser-xhr": "4.24.0", - "@algolia/requester-common": "4.24.0", - "@algolia/requester-node-http": "4.24.0", - "@algolia/transporter": "4.24.0" + "@algolia/client-abtesting": "5.1.0", + "@algolia/client-analytics": "5.1.0", + "@algolia/client-common": "5.1.0", + "@algolia/client-personalization": "5.1.0", + "@algolia/client-search": "5.1.0", + "@algolia/recommend": "5.1.0", + "@algolia/requester-browser-xhr": "5.1.0", + "@algolia/requester-node-http": "5.1.0" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/algoliasearch-helper": { - "version": "3.22.3", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.22.3.tgz", - "integrity": "sha512-2eoEz8mG4KHE+DzfrBTrCmDPxVXv7aZZWPojAJFtARpxxMO6lkos1dJ+XDCXdPvq7q3tpYWRi6xXmVQikejtpA==", + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.22.4.tgz", + "integrity": "sha512-fvBCywguW9f+939S6awvRMstqMF1XXcd2qs1r1aGqL/PJ1go/DqN06tWmDVmhCDqBJanm++imletrQWf0G2S1g==", "license": "MIT", "dependencies": { "@algolia/events": "^4.0.1" @@ -6342,9 +6263,9 @@ "license": "MIT" }, "node_modules/chart.js": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz", - "integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.4.tgz", + "integrity": "sha512-emICKGBABnxhMjUjlYRR12PmOXhJ2eJjEHL2/dZlWjxRAZT1D8xplLFq5M0tMQK8ja+wBS/tuVEJB5C6r7VxJA==", "license": "MIT", "dependencies": { "@kurkle/color": "^0.3.0" @@ -6817,9 +6738,9 @@ } }, "node_modules/core-js-compat": { - "version": "3.38.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.0.tgz", - "integrity": "sha512-75LAicdLa4OJVwFxFbQR3NdnZjNgX6ILpVcVzcC4T2smerB5lELMrJQQQoWV6TiuC/vlaFqgU2tKQx9w5s0e0A==", + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", + "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==", "dev": true, "license": "MIT", "dependencies": { @@ -8985,9 +8906,9 @@ "license": "BSD-3-Clause" }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", "engines": { @@ -9125,29 +9046,29 @@ } }, "node_modules/instantsearch-ui-components": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/instantsearch-ui-components/-/instantsearch-ui-components-0.8.0.tgz", - "integrity": "sha512-EzV7cR5+18sjmR6DMdv8yL9WuS2hUxrkqbByiLmHnJFbB4TZ4Q7oZDAn43bOItWZ2TxMK3GoxNbB/ZhWjsptPg==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/instantsearch-ui-components/-/instantsearch-ui-components-0.9.0.tgz", + "integrity": "sha512-ugQ+XdPx3i3Sxu+woRo6tPE0Fz/kWd4KblTUfZD1TZZBsm/8qFvcbg5dVBDvXX9v7ntoyugXCzC/XCZMzrSkig==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.1.2" } }, "node_modules/instantsearch.js": { - "version": "4.73.4", - "resolved": "https://registry.npmjs.org/instantsearch.js/-/instantsearch.js-4.73.4.tgz", - "integrity": "sha512-QdvExJthRBXpRaX9lzey+2sqUIzlOZEpd8N5wZyLYYs6WjDHIwrNPOzmOv7VHLBBHGqZ6YkXoCoegj5zm9QI8g==", + "version": "4.74.0", + "resolved": "https://registry.npmjs.org/instantsearch.js/-/instantsearch.js-4.74.0.tgz", + "integrity": "sha512-IbKAvnQ03cxb1Ni1OpLv6Yuu1W7Cu1zGru77rvgzYyPsurknpjQHdBicszSZlKaK/zND7D5vhSNZoliiz9nuEQ==", "license": "MIT", "dependencies": { "@algolia/events": "^4.0.1", "@types/dom-speech-recognition": "^0.0.1", - "@types/google.maps": "^3.45.3", + "@types/google.maps": "^3.55.12", "@types/hogan.js": "^3.0.0", "@types/qs": "^6.5.3", - "algoliasearch-helper": "3.22.3", + "algoliasearch-helper": "3.22.4", "hogan.js": "^3.0.2", "htm": "^3.0.0", - "instantsearch-ui-components": "0.8.0", + "instantsearch-ui-components": "0.9.0", "preact": "^10.10.0", "qs": "^6.5.1 < 6.10", "search-insights": "^2.15.0" @@ -11304,9 +11225,9 @@ } }, "node_modules/npm-check-updates": { - "version": "17.0.6", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-17.0.6.tgz", - "integrity": "sha512-KCiaJH1cfnh/RyzKiDNjNfXgcKFyQs550Uf1OF/Yzb8xO56w+RLpP/OKRUx23/GyP/mLYwEpOO65qjmVdh6j0A==", + "version": "17.1.0", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-17.1.0.tgz", + "integrity": "sha512-RcohCA/tdpxyPllBlYDkqGXFJQgTuEt0f2oPSL9s05pZ3hxYdleaUtvEcSxKl0XAg3ncBhVgLAxhXSjoryUU5Q==", "license": "Apache-2.0", "bin": { "ncu": "build/cli.js", @@ -12180,9 +12101,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.1.tgz", - "integrity": "sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dev": true, "license": "MIT", "dependencies": { @@ -12819,9 +12740,9 @@ "license": "MIT" }, "node_modules/sass": { - "version": "1.77.8", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz", - "integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==", + "version": "1.77.6", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.6.tgz", + "integrity": "sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==", "dev": true, "license": "MIT", "dependencies": { @@ -12963,9 +12884,9 @@ } }, "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -15567,9 +15488,9 @@ } }, "node_modules/zone.js": { - "version": "0.14.10", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.14.10.tgz", - "integrity": "sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==", + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.0.tgz", + "integrity": "sha512-9oxn0IIjbCZkJ67L+LkhYWRyAy7axphb3VgE2MBDlOqnmHMPWGYMxJxBYFueFq/JGY2GMwS0rU+UCLunEmy5UA==", "license": "MIT" } } diff --git a/client/package.json b/client/package.json index 63808ec1..b48bb592 100644 --- a/client/package.json +++ b/client/package.json @@ -10,33 +10,33 @@ }, "private": true, "dependencies": { - "@angular/animations": "^18.2.0", - "@angular/cdk": "^18.2.0", - "@angular/common": "^18.2.0", - "@angular/compiler": "^18.2.0", - "@angular/core": "^18.2.0", - "@angular/forms": "^18.2.0", - "@angular/localize": "^18.2.0", - "@angular/material": "^18.2.0", - "@angular/platform-browser": "^18.2.0", - "@angular/platform-browser-dynamic": "^18.2.0", - "@angular/router": "^18.2.0", + "@angular/animations": "^18.2.1", + "@angular/cdk": "^18.2.1", + "@angular/common": "^18.2.1", + "@angular/compiler": "^18.2.1", + "@angular/core": "^18.2.1", + "@angular/forms": "^18.2.1", + "@angular/localize": "^18.2.1", + "@angular/material": "^18.2.1", + "@angular/platform-browser": "^18.2.1", + "@angular/platform-browser-dynamic": "^18.2.1", + "@angular/router": "^18.2.1", "@fortawesome/fontawesome-free": "^6.6.0", "@ng-bootstrap/ng-bootstrap": "^17.0.0", "@swimlane/ngx-charts": "^20.5.0", "@tsparticles/angular": "^3.0.0", "angular-auth-oidc-client": "^18.0.1", "bootstrap": "^5.3.3", - "chart.js": "^4.4.3", + "chart.js": "^4.4.4", "chartjs-plugin-datalabels": "^2.2.0", "classlist.js": "^1.1.20150312", "date-fns": "^3.6.0", "exceljs": "^4.4.0", - "instantsearch.js": "^4.73.4", + "instantsearch.js": "^4.74.0", "json-to-plain-text": "^1.1.4", "jsonwebtoken": "^9.0.2", "ng2-charts": "^6.0.1", - "npm-check-updates": "^17.0.6", + "npm-check-updates": "^17.1.0", "pace-js": "^1.2.4", "primeflex": "^3.3.1", "primeicons": "^7.0.0", @@ -45,17 +45,17 @@ "subsink": "^1.0.2", "typescript": "^5.5.4", "web-animations-js": "^2.3.2", - "zone.js": "^0.14.10" + "zone.js": "^0.15.0" }, "devDependencies": { - "@angular-devkit/build-angular": "^18.2.0", - "@angular/cli": "^18.2.0", - "@angular/compiler-cli": "^18.2.0", + "@angular-devkit/build-angular": "^18.2.1", + "@angular/cli": "^18.2.1", + "@angular/compiler-cli": "^18.2.1", "@types/chart.js": "^2.9.41", "@types/d3": "^7.4.3", "@types/jasmine": "^5.1.4", "@types/jquery": "^3.5.30", - "@types/node": "^22.4.1", + "@types/node": "^22.5.0", "jasmine-core": "^5.2.0" } } diff --git a/client/src/app/app-routing.module.ts b/client/src/app/app-routing.module.ts index 8823790d..b11bb4bb 100644 --- a/client/src/app/app-routing.module.ts +++ b/client/src/app/app-routing.module.ts @@ -13,17 +13,19 @@ import { ExtraOptions, RouterModule, Routes } from '@angular/router'; import { NotificationsComponent } from './common/components/notifications/notifications.component'; import { AuthGuard } from './core/auth/guards/auth.guard' import { UnauthorizedComponent } from './common/components/unauthorized/unauthorized.component'; +import { NotActivatedComponent } from './common/components/notActivated/notActivated.component'; import { AppLayoutComponent } from './layout/components/app.layout.component'; +import { DoDConsentComponent } from './common/components/dod-consent/dod-consent.component'; const routerOptions: ExtraOptions = { anchorScrolling: 'enabled' }; const routes: Routes = [ - { + { path: '', component: AppLayoutComponent, - children: [ - + children: + [ { path: 'admin-processing', canActivate: [AuthGuard], data: { guardType: 'admin' }, loadChildren: () => import('./pages/admin-processing/admin-processing.module').then(m => m.AdminProcessingModule) }, { path: 'asset-processing', canActivate: [AuthGuard], loadChildren: () => import('./pages/asset-processing/asset-processing.module').then(m => m.AssetProcessingModule) }, { path: 'consent', canActivate: [AuthGuard], loadChildren: () => import('./common/components/dod-consent/dod-consent.module').then(m => m.DoDConsentModule) }, @@ -35,6 +37,7 @@ const routes: Routes = [ ] }, { path: 'unauthorized', component: UnauthorizedComponent }, + { path: 'not-activated', component: NotActivatedComponent }, { path: '**', redirectTo: 'consent' }, ]; @NgModule({ diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index cd651cfe..451c0fb0 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts @@ -31,9 +31,7 @@ export class AppComponent implements OnInit { try { await this.authService.initializeAuthentication(); this.userProfile = await this.authService.getUserData('cpat'); - } catch (error) { - console.error('Authentication Error:', error); - } finally { + const apiConfig = await this.sharedService.getApiConfig().toPromise(); if (apiConfig && typeof apiConfig === 'object' && 'classification' in apiConfig) { const apiClassification = (apiConfig as { classification: string }).classification; @@ -41,6 +39,8 @@ export class AppComponent implements OnInit { } else { console.error('Invalid API configuration response'); } + } catch (error) { + console.error('Authentication Error:', error); } } } diff --git a/client/src/app/app.module.ts b/client/src/app/app.module.ts index 88842ef3..4ab540f0 100644 --- a/client/src/app/app.module.ts +++ b/client/src/app/app.module.ts @@ -24,6 +24,7 @@ import { AppComponent } from './app.component'; import { NgxParticlesModule } from "@tsparticles/angular"; import { NotFoundComponent } from './common/components/not-found/not-found.component'; import { UnauthorizedComponent } from './common/components/unauthorized/unauthorized.component'; +import { NotActivatedComponent } from './common/components/notActivated/notActivated.component'; import { ButtonModule } from 'primeng/button'; import { AvatarModule } from 'primeng/avatar'; import { MenuModule } from 'primeng/menu'; @@ -34,6 +35,7 @@ import { OverlayPanelModule } from 'primeng/overlaypanel'; import { RippleModule } from 'primeng/ripple'; import { SelectButtonModule } from 'primeng/selectbutton'; import { PrimeNGConfig } from 'primeng/api'; + function getScopeStr(configId: string) { const cpatScopePrefix = CPAT.Env.oauth.scopePrefix; const stigmanScopePrefix = CPAT.Env.stigman.scopePrefix; @@ -72,7 +74,8 @@ function getScopeStr(configId: string) { declarations: [ AppComponent, NotFoundComponent, - UnauthorizedComponent + UnauthorizedComponent, + NotActivatedComponent, ], imports: [ BrowserModule, @@ -87,10 +90,10 @@ function getScopeStr(configId: string) { config: [ { configId: 'cpat', - postLoginRoute: '/consent', + postLoginRoute: '/verify', authority: CPAT.Env.oauth.authority, - redirectUrl: window.location.origin + '/consent', - postLogoutRedirectUri: window.location.origin + '/consent', + redirectUrl: window.location.origin + '/verify', + postLogoutRedirectUri: window.location.origin + '/verify', clientId: CPAT.Env.oauth.clientId, scope: getScopeStr('cpat'), responseType: 'code', @@ -105,8 +108,8 @@ function getScopeStr(configId: string) { { configId: 'stigman', authority: CPAT.Env.oauth.authority, - redirectUrl: window.location.origin + '/consent', - postLogoutRedirectUri: window.location.origin + '/consent', + redirectUrl: window.location.origin + '/verify', + postLogoutRedirectUri: window.location.origin + '/verify', clientId: CPAT.Env.stigman.clientId, scope: getScopeStr('stigman'), responseType: 'code', diff --git a/client/src/app/common/components/dod-consent/dod-consent.component.ts b/client/src/app/common/components/dod-consent/dod-consent.component.ts index bc208f32..e79cf822 100644 --- a/client/src/app/common/components/dod-consent/dod-consent.component.ts +++ b/client/src/app/common/components/dod-consent/dod-consent.component.ts @@ -36,7 +36,6 @@ export class DoDConsentComponent implements AfterViewInit { consentOk() { if (this.modalWindow) { this.modalWindow.close(); - this.router.navigate(['/poam-processing']); } } } diff --git a/client/src/app/common/components/not-found/not-found.component.html b/client/src/app/common/components/not-found/not-found.component.html index 1a3c20d1..69179759 100644 --- a/client/src/app/common/components/not-found/not-found.component.html +++ b/client/src/app/common/components/not-found/not-found.component.html @@ -189,7 +189,7 @@

LOST IN SPACE C-PAT? Hmm, looks like that page doesn't exist. - + diff --git a/client/src/app/common/components/notActivated/notActivated.component.html b/client/src/app/common/components/notActivated/notActivated.component.html new file mode 100644 index 00000000..23f88a86 --- /dev/null +++ b/client/src/app/common/components/notActivated/notActivated.component.html @@ -0,0 +1,196 @@ + + + +
+
+ +
+
+

403

+

LOST IN SPACE C-PAT? Hmm, looks like your account has not been activated. Please contact your C-PAT Administrator.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
diff --git a/client/src/app/common/components/notActivated/notActivated.component.scss b/client/src/app/common/components/notActivated/notActivated.component.scss new file mode 100644 index 00000000..431a9823 --- /dev/null +++ b/client/src/app/common/components/notActivated/notActivated.component.scss @@ -0,0 +1,155 @@ +/* +!####################################################################### +! C-PATTM SOFTWARE +! CRANE C-PATTM plan of action and milestones software. Use is governed by the Open Source Academic Research License Agreement contained in the file +! crane_C_PAT.1_license.txt, which is part of this software package. BY +! USING OR MODIFYING THIS SOFTWARE, YOU ARE AGREEING TO THE TERMS AND +! CONDITIONS OF THE LICENSE. +!######################################################################## +*/ + +.particle-error, +.permission_denied, +#particles-js { + width: 100%; + height: 100%; + margin: 0px !important; +} + +#particles-js { + position: fixed !important; + opacity: 0.23; +} + +.permission_denied { + background: var(--surface-a); +} + +.permission_denied a { + text-decoration: none; +} + +.denied__wrapper { + max-width: 35rem; + width: 100%; + max-height: 60rem; + display: block; + margin: 0 auto; + position: relative; + margin-top: 8vh; +} + +.permission_denied h1 { + text-align: center; + color: var(--text-color); + opacity: 0.8; + font-family: "Dosis", sans-serif; + font-size: 100px; + margin-bottom: 0px; + font-weight: 800; +} + +.permission_denied h3 { + text-align: center; + color: var(--text-color); + font-size: 19px; + line-height: 23px; + max-width: 330px; + margin: 0px auto 30px auto; + font-family: "Dosis", sans-serif; + font-weight: 400; +} + +.permission_denied h3 span { + position: relative; + width: 65px; + display: inline-block; +} + +.permission_denied h3 span:after { + content: ""; + border-bottom: 3px solid #ffbb39; + position: absolute; + left: 0; + top: 43%; + width: 100%; +} + +.denied__link { + margin: 0 auto; + vertical-align: middle; + display: block; + margin-bottom: 40px; + margin-top: 25px; +} + +.permission_denied .stars { + animation: sparkle 1.6s infinite ease-in-out alternate; +} + +@keyframes sparkle { + 0% { + opacity: 1; + } + + 100% { + opacity: 0.3; + } +} + +#astronaut { + width: 43px; + position: absolute; + right: 20px; + top: 210px; + animation: spin 4.5s infinite linear; +} + +@keyframes spin { + 0% { + transform: rotateZ(0deg); + } + + 100% { + transform: rotateZ(360deg); + } +} + +@media (max-width: 600px) { + .permission_denied h1 { + font-size: 75px; + } + + .permission_denied h3 { + font-size: 16px; + width: 200px; + margin: 0 auto; + line-height: 23px; + } + + .permission_denied h3 span { + width: 60px; + } + + #astronaut { + width: 35px; + right: 40px; + top: 170px; + } +} + +.saturn, +.saturn-2, +.hover { + animation: hover 2s infinite ease-in-out alternate; +} + +@keyframes hover { + 0% { + transform: translateY(3px); + } + + 100% { + transform: translateY(-3px); + } +} diff --git a/client/src/app/common/components/notActivated/notActivated.component.ts b/client/src/app/common/components/notActivated/notActivated.component.ts new file mode 100644 index 00000000..3f382e23 --- /dev/null +++ b/client/src/app/common/components/notActivated/notActivated.component.ts @@ -0,0 +1,138 @@ +/* +!####################################################################### +! C-PATTM SOFTWARE +! CRANE C-PATTM plan of action and milestones software. Use is governed by the Open Source Academic Research License Agreement contained in the file +! crane_C_PAT.1_license.txt, which is part of this software package. BY +! USING OR MODIFYING THIS SOFTWARE, YOU ARE AGREEING TO THE TERMS AND +! CONDITIONS OF THE LICENSE. +!######################################################################## +*/ + +import { Component } from '@angular/core'; +import { AuthService } from '../../../core/auth/services/auth.service'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'cpat-not-activated', + styleUrls: ['./notActivated.component.scss'], + templateUrl: './notActivated.component.html', +}) +export class NotActivatedComponent { + particlesOptions: object; + + constructor( + private authService: AuthService, + private router: Router + ) { + this.particlesOptions = { + particles: { + number: { + value: 160, + density: { + enable: true, + value_area: 800 + } + }, + color: { + value: '#ffffff' + }, + shape: { + type: 'circle', + stroke: { + width: 0, + color: '#000000' + }, + polygon: { + nb_sides: 5 + }, + image: { + src: 'img/github.svg', + width: 100, + height: 100 + } + }, + opacity: { + value: 1, + random: true, + anim: { + enable: true, + speed: 1, + opacity_min: 0, + sync: false + } + }, + size: { + value: 3, + random: true, + anim: { + enable: false, + speed: 4, + size_min: 0.3, + sync: false + } + }, + line_linked: { + enable: false, + distance: 150, + color: '#ffffff', + opacity: 0.4, + width: 1 + }, + move: { + enable: true, + speed: 0.17, + direction: 'none', + random: true, + straight: false, + out_mode: 'out', + bounce: false, + attract: { + enable: false, + rotateX: 600, + rotateY: 600 + } + } + }, + interactivity: { + detect_on: 'canvas', + events: { + onhover: { + enable: false, + mode: 'bubble' + }, + onclick: { + enable: false, + mode: 'repulse' + }, + resize: false + }, + modes: { + grab: { + distance: 400, + line_linked: { + opacity: 1 + } + }, + bubble: { + distance: 250, + size: 0, + duration: 2, + opacity: 0, + speed: 3 + }, + repulse: { + distance: 400, + duration: 0.4 + }, + push: { + particles_nb: 4 + }, + remove: { + particles_nb: 2 + } + } + }, + retina_detect: true + }; + } +} diff --git a/client/src/app/common/components/notifications/notifications-popover/notifications-popover.component.ts b/client/src/app/common/components/notifications/notifications-popover/notifications-popover.component.ts index 7e40e299..2f8dbc1c 100644 --- a/client/src/app/common/components/notifications/notifications-popover/notifications-popover.component.ts +++ b/client/src/app/common/components/notifications/notifications-popover/notifications-popover.component.ts @@ -81,7 +81,7 @@ export class NotificationsPanelComponent implements OnInit { } async fetchNotifications() { - (await this.notificationService.getUnreadNotificationsByUserId(this.user.userId)).subscribe( + (await this.notificationService.getUnreadNotifications()).subscribe( notifications => { this.notifications = notifications; }, @@ -92,7 +92,7 @@ export class NotificationsPanelComponent implements OnInit { } async dismissNotification(notification: any) { - (await this.notificationService.dismissNotificationByNotificationId(notification.notificationId)).subscribe( + (await this.notificationService.dismissNotification(notification.notificationId)).subscribe( () => { const index = this.notifications.indexOf(notification); if (index !== -1) { @@ -106,7 +106,7 @@ export class NotificationsPanelComponent implements OnInit { } async dismissAllNotifications() { - (await this.notificationService.dismissAllNotificationsByUserId(this.user.userId)).subscribe( + (await this.notificationService.dismissAllNotifications()).subscribe( () => { this.notifications = []; }, diff --git a/client/src/app/common/components/notifications/notifications.component.html b/client/src/app/common/components/notifications/notifications.component.html index 0198bfd3..4e26d245 100644 --- a/client/src/app/common/components/notifications/notifications.component.html +++ b/client/src/app/common/components/notifications/notifications.component.html @@ -55,7 +55,7 @@

{{ notification.title }}

(`${this.cpatApiBase}/notifications/all/${userId}`, { headers }) + return this.http.get(`${this.cpatApiBase}/notifications/all`, { headers }) .pipe(catchError(this.handleError)); } - async getUnreadNotificationsByUserId(userId: number) { + async getUnreadNotifications() { const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/notifications/unread/${userId}`, { headers }) + return this.http.get(`${this.cpatApiBase}/notifications/unread`, { headers }) .pipe(catchError(this.handleError)); } - async getUnreadNotificationCountByUserId(userId: number) { + async getUnreadNotificationCount() { const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/notifications/unread/count/${userId}`, { headers }) + return this.http.get(`${this.cpatApiBase}/notifications/unread/count`, { headers }) .pipe(catchError(this.handleError)); } - async dismissNotificationByNotificationId(notificationId: number) { + async dismissNotification(notificationId: number) { const headers = await this.getAuthHeaders(); return this.http.put(`${this.cpatApiBase}/notifications/dismiss/${notificationId}`, null, { headers }) .pipe(catchError(this.handleError)); } - async dismissAllNotificationsByUserId(userId: number) { + async dismissAllNotifications() { const headers = await this.getAuthHeaders(); - return this.http.put(`${this.cpatApiBase}/notifications/all/dismiss/${userId}`, null, { headers }) + return this.http.put(`${this.cpatApiBase}/notifications/all/dismiss`, null, { headers }) .pipe(catchError(this.handleError)); } - async deleteNotificationByNotificationId(notificationId: number) { + async deleteNotification(notificationId: number) { const headers = await this.getAuthHeaders(); return this.http.delete(`${this.cpatApiBase}/notifications/delete/${notificationId}`, { headers }) .pipe(catchError(this.handleError)); } - async deleteAllNotificationsByUserId(userId: number) { + async deleteAllNotifications() { const headers = await this.getAuthHeaders(); - return this.http.delete(`${this.cpatApiBase}/notifications/all/delete/${userId}`, { headers }) + return this.http.delete(`${this.cpatApiBase}/notifications/all/delete`, { headers }) .pipe(catchError(this.handleError)); } } diff --git a/client/src/app/common/components/search/app.search.component.ts b/client/src/app/common/components/search/app.search.component.ts index 9182a57b..913840c8 100644 --- a/client/src/app/common/components/search/app.search.component.ts +++ b/client/src/app/common/components/search/app.search.component.ts @@ -14,23 +14,23 @@ interface SearchItem { standalone: true, imports: [CommonModule, FormsModule, AutoCompleteModule], template: ` - - - -
{{item.title}}
-
-
- -
+ + + +
{{item.title}}
+
+
+ +
`, styles: [` :host ::ng-deep .p-autocomplete { @@ -45,19 +45,27 @@ export class AppSearchComponent { public filteredItems: SearchItem[] = []; public query: string = ''; public placeholder: string = 'Search...'; + private searchItems: SearchItem[] = []; - private searchItems: SearchItem[] = [ - { title: 'Add POAM', path: '/poam-processing/poam-details/ADDPOAM' }, - { title: 'Asset Processing', path: '/asset-processing' }, - { title: 'Home', path: '/poam-processing' }, - { title: 'Import Processing', path: '/import-processing' }, - { title: 'Label Processing', path: '/label-processing' }, - { title: 'Manage POAMs', path: '/poam-processing/poam-manage' }, - { title: 'Marketplace', path: '/marketplace' }, - { title: 'Notifications', path: '/notifications' }, - ]; + constructor(private router: Router) { + this.initializeSearchItems(); + } + + private initializeSearchItems(): void { + this.searchItems = [ + { title: 'Add POAM', path: '/poam-processing/poam-details/ADDPOAM' }, + { title: 'Asset Processing', path: '/asset-processing' }, + { title: 'Home', path: '/poam-processing' }, + { title: 'Import Processing', path: '/import-processing' }, + { title: 'Label Processing', path: '/label-processing' }, + { title: 'Manage POAMs', path: '/poam-processing/poam-manage' }, + { title: 'Notifications', path: '/notifications' }, + ]; - constructor(private router: Router) { } + if (!CPAT.Env.features.marketplaceDisabled) { + this.searchItems.push({ title: 'Marketplace', path: '/marketplace' }); + } + } search(event: { query: string }) { this.filteredItems = this.searchItems.filter(item => diff --git a/client/src/app/common/components/unauthorized/unauthorized.component.html b/client/src/app/common/components/unauthorized/unauthorized.component.html index e64223a5..41c3165a 100644 --- a/client/src/app/common/components/unauthorized/unauthorized.component.html +++ b/client/src/app/common/components/unauthorized/unauthorized.component.html @@ -189,7 +189,7 @@

LOST IN SPACE C-PAT? Hmm, looks like that page doesn't exist. - +

diff --git a/client/src/app/common/utils/excel-data.service.ts b/client/src/app/common/utils/excel-data.service.ts index ff4d350b..67df2f9e 100644 --- a/client/src/app/common/utils/excel-data.service.ts +++ b/client/src/app/common/utils/excel-data.service.ts @@ -27,7 +27,6 @@ interface Poam { requiredResources: string; residualRisk: string; status: string; - vulnIdRestricted: string; submittedDate: Date | string; emassPoamId: string; securityControlNumber: string; @@ -40,8 +39,8 @@ interface Poam { likelihood: string; relevanceOfThreat: string; devicesAffected: string; - businessImpactRating: string; - businessImpactDescription: string; + impactRating: string; + impactDescription: string; extensionTimeAllowed: number; extensionJustification: string; } @@ -77,8 +76,8 @@ export class ExcelDataService { "S": "relevanceOfThreat", "T": "threatDescription", "U": "likelihood", - "V": "businessImpactRating", - "W": "businessImpactDescription", + "V": "impactRating", + "W": "impactDescription", "X": "residualRisk", "Y": "recommendations", "Z": "adjSeverity" diff --git a/client/src/app/core/auth/services/auth.service.ts b/client/src/app/core/auth/services/auth.service.ts index 869b8ccd..27e240a8 100644 --- a/client/src/app/core/auth/services/auth.service.ts +++ b/client/src/app/core/auth/services/auth.service.ts @@ -27,7 +27,6 @@ export class AuthService { } else if (!isAuthenticatedCpat) { await this.oidcSecurityService.authorize('cpat'); } else { - (await this.usersService.loginState("logIn")).subscribe((result: any) => console.log("[C-PAT] ", result.message)); this.router.navigate(['/poam-processing']); } } @@ -64,7 +63,6 @@ export class AuthService { } async logout() { - await (await this.usersService.loginState("logOut")).subscribe((result: any) => console.log("[C-PAT] ", result.message)); await this.oidcSecurityService.logoff('stigman', undefined); await this.oidcSecurityService.logoff('cpat', undefined).subscribe((result) => console.log(result)); } diff --git a/client/src/app/layout/components/app.layout.component.html b/client/src/app/layout/components/app.layout.component.html index 80c8f176..7aba91e8 100644 --- a/client/src/app/layout/components/app.layout.component.html +++ b/client/src/app/layout/components/app.layout.component.html @@ -1,4 +1,4 @@ -
+
diff --git a/client/src/app/layout/components/app.layout.component.ts b/client/src/app/layout/components/app.layout.component.ts index 4fdd7322..70aabd32 100644 --- a/client/src/app/layout/components/app.layout.component.ts +++ b/client/src/app/layout/components/app.layout.component.ts @@ -4,84 +4,112 @@ import { filter, Subscription } from 'rxjs'; import { MenuService } from '../services/app.menu.service'; import { AppNavigationComponent } from './app.navigation.component'; import { LayoutService } from '../services/app.layout.service'; +import { SubSink } from 'subsink'; +import { UsersService } from '../../pages/admin-processing/user-processing/users.service'; @Component({ selector: 'app-layout', templateUrl: './app.layout.component.html', }) export class AppLayoutComponent implements OnDestroy { - overlayMenuOpenSubscription: Subscription; - - menuOutsideClickListener: any; - - menuScrollListener: any; - - @ViewChild(AppNavigationComponent) appSidebar!: AppNavigationComponent; - - @ViewChild(AppNavigationComponent) appTopbar!: AppNavigationComponent; - - constructor( - private menuService: MenuService, - public layoutService: LayoutService, - public renderer: Renderer2, - public router: Router - ) { - this.overlayMenuOpenSubscription = - this.layoutService.overlayOpen$.subscribe(() => { - if (!this.menuOutsideClickListener) { - this.menuOutsideClickListener = this.renderer.listen( - 'document', - 'click', - (event) => { - const isOutsideClicked = !( - this.appSidebar.el.nativeElement.isSameNode( - event.target - ) || - this.appSidebar.el.nativeElement.contains( - event.target - ) || - this.appTopbar.menuButton.nativeElement.isSameNode( - event.target - ) || - this.appTopbar.menuButton.nativeElement.contains( - event.target - ) - ); - if (isOutsideClicked) { - this.hideMenu(); - } - } - ); - } - - if ( - (this.layoutService.isHorizontal() || - this.layoutService.isSlim() || - this.layoutService.isSlimPlus()) && - !this.menuScrollListener - ) { - this.menuScrollListener = this.renderer.listen( - this.appSidebar.menuContainer.nativeElement, - 'scroll', - (event) => { - if (this.layoutService.isDesktop()) { - this.hideMenu(); - } - } - ); - } - - if (this.layoutService.state.staticMenuMobileActive) { - this.blockBodyScroll(); - } - }); - - this.router.events - .pipe(filter((event) => event instanceof NavigationEnd)) - .subscribe(() => { + overlayMenuOpenSubscription: Subscription; + menuOutsideClickListener: any; + menuScrollListener: any; + user: any; + verified: boolean = false; + private subs = new SubSink(); + @ViewChild(AppNavigationComponent) appSidebar!: AppNavigationComponent; + @ViewChild(AppNavigationComponent) appTopbar!: AppNavigationComponent; + + constructor( + private menuService: MenuService, + public layoutService: LayoutService, + public renderer: Renderer2, + public router: Router, + private userService: UsersService + ) { + this.initialize(); + } + + async initialize() { + try { + this.user = null; + this.subs.sink = (await this.userService.getCurrentUser()).subscribe({ + next: (response: any) => { + this.user = response; + if (this.user.accountStatus != 'ACTIVE') { + this.router.navigate(['/not-activated']); + } else { + this.verified = true; + this.continueInitialization(); + } + }, + error: (error: any) => { + console.error('An error occurred:', error.message); + } + }); + } catch (error) { + console.error('Error initializing user:', error); + } + } + + continueInitialization() { + this.overlayMenuOpenSubscription = + this.layoutService.overlayOpen$.subscribe(() => { + if (!this.menuOutsideClickListener) { + this.menuOutsideClickListener = this.renderer.listen( + 'document', + 'click', + (event) => { + const isOutsideClicked = !( + this.appSidebar.el.nativeElement.isSameNode( + event.target + ) || + this.appSidebar.el.nativeElement.contains( + event.target + ) || + this.appTopbar.menuButton.nativeElement.isSameNode( + event.target + ) || + this.appTopbar.menuButton.nativeElement.contains( + event.target + ) + ); + if (isOutsideClicked) { this.hideMenu(); - }); - } + } + } + ); + } + + if ( + (this.layoutService.isHorizontal() || + this.layoutService.isSlim() || + this.layoutService.isSlimPlus()) && + !this.menuScrollListener + ) { + this.menuScrollListener = this.renderer.listen( + this.appSidebar.menuContainer.nativeElement, + 'scroll', + (event) => { + if (this.layoutService.isDesktop()) { + this.hideMenu(); + } + } + ); + } + + if (this.layoutService.state.staticMenuMobileActive) { + this.blockBodyScroll(); + } + }); + + this.router.events + .pipe(filter((event) => event instanceof NavigationEnd)) + .subscribe(() => { + this.hideMenu(); + }); + } blockBodyScroll(): void { if (document.body.classList) { @@ -107,25 +135,25 @@ export class AppLayoutComponent implements OnDestroy { } } - hideMenu() { - this.layoutService.state.overlayMenuActive = false; - this.layoutService.state.staticMenuMobileActive = false; - this.layoutService.state.menuHoverActive = false; - this.menuService.reset(); + hideMenu() { + this.layoutService.state.overlayMenuActive = false; + this.layoutService.state.staticMenuMobileActive = false; + this.layoutService.state.menuHoverActive = false; + this.menuService.reset(); - if (this.menuOutsideClickListener) { - this.menuOutsideClickListener(); - this.menuOutsideClickListener = null; - } - - if (this.menuScrollListener) { - this.menuScrollListener(); - this.menuScrollListener = null; - } + if (this.menuOutsideClickListener) { + this.menuOutsideClickListener(); + this.menuOutsideClickListener = null; + } - this.unblockBodyScroll(); + if (this.menuScrollListener) { + this.menuScrollListener(); + this.menuScrollListener = null; } + this.unblockBodyScroll(); + } + get containerClass() { return { 'layout-light': this.layoutService.config().colorScheme === 'light', @@ -159,13 +187,13 @@ export class AppLayoutComponent implements OnDestroy { }; } - ngOnDestroy() { - if (this.overlayMenuOpenSubscription) { - this.overlayMenuOpenSubscription.unsubscribe(); - } + ngOnDestroy() { + if (this.overlayMenuOpenSubscription) { + this.overlayMenuOpenSubscription.unsubscribe(); + } - if (this.menuOutsideClickListener) { - this.menuOutsideClickListener(); - } + if (this.menuOutsideClickListener) { + this.menuOutsideClickListener(); } + } } diff --git a/client/src/app/layout/components/app.navigation.component.html b/client/src/app/layout/components/app.navigation.component.html index 3d2c6578..464aac87 100644 --- a/client/src/app/layout/components/app.navigation.component.html +++ b/client/src/app/layout/components/app.navigation.component.html @@ -14,6 +14,7 @@ optionLabel="collectionName" (onChange)="resetWorkspace($event.value.collectionId)" appendTo="body" + emptyMessage="No collections assigned..." [style]="{'height': '85%', 'width': '94%', 'align-items': 'center', 'margin-left': '3%', 'border-radius': '10px'}">
diff --git a/client/src/app/layout/components/app.navigation.component.ts b/client/src/app/layout/components/app.navigation.component.ts index ae66cfef..f19ff0a4 100644 --- a/client/src/app/layout/components/app.navigation.component.ts +++ b/client/src/app/layout/components/app.navigation.component.ts @@ -10,20 +10,7 @@ import { SubSink } from 'subsink'; import { SharedService } from '../../common/services/shared.service'; import { format } from 'date-fns'; import { Subject, filter, takeUntil } from 'rxjs'; -function getRoleFromAccessLevel(accessLevel: number): string { - switch (accessLevel) { - case 1: - return 'viewer'; - case 2: - return 'submitter'; - case 3: - return 'approver'; - case 4: - return 'cat1approver'; - default: - return 'none'; - } -} + interface Permission { userId: number; collectionId: number; @@ -66,8 +53,6 @@ export class AppNavigationComponent implements OnInit, OnDestroy { public async ngOnInit() { this.layoutService.setInitialTheme('lara-dark-blue'); this.initializeUser(); - this.setMenuItems(); - this.setupUserMenuActions(); } async initializeUser() { @@ -92,6 +77,8 @@ export class AppNavigationComponent implements OnInit, OnDestroy { }; this.getNotificationCount(); this.getCollections(); + this.setMenuItems(); + this.setupUserMenuActions(); this.router.events.pipe( filter(event => event instanceof NavigationEnd), takeUntil(this.destroy$) @@ -100,8 +87,6 @@ export class AppNavigationComponent implements OnInit, OnDestroy { this.getNotificationCount(); } }); - } else { - alert('Your account status is not Active, contact your system administrator'); } }, error: (error) => { @@ -114,8 +99,7 @@ export class AppNavigationComponent implements OnInit, OnDestroy { } async getCollections() { - const userName = this.payload.userName; - this.subs.sink = (await this.collectionService.getCollections(userName)).subscribe((result: any) => { + this.subs.sink = (await this.collectionService.getCollections()).subscribe((result: any) => { this.collections = result; if (this.user.lastCollectionAccessedId) { this.selectedCollection = +this.user.lastCollectionAccessedId; @@ -130,7 +114,7 @@ export class AppNavigationComponent implements OnInit, OnDestroy { } async getNotificationCount() { - this.subs.sink = (await this.notificationService.getUnreadNotificationCountByUserId(this.user.userId)).subscribe((result: any) => { + this.subs.sink = (await this.notificationService.getUnreadNotificationCount()).subscribe((result: any) => { this.notificationCount = result > 0 ? result : null; }); } @@ -227,22 +211,8 @@ export class AppNavigationComponent implements OnInit, OnDestroy { officeOrg: this.user.officeOrg, defaultTheme: this.user.defaultTheme || 'default', isAdmin: this.user.isAdmin, + points: this.user.points, }; - const selectedPermissions = this.payload.collections.find((x: { collectionId: any; }) => x.collectionId == selectedCollection); - let myRole: string; - - if (!selectedPermissions && !this.user.isAdmin) { - myRole = 'none'; - } else if (this.user.isAdmin) { - myRole = 'admin'; - } else if (selectedPermissions) { - myRole = getRoleFromAccessLevel(selectedPermissions.accessLevel); - } else { - myRole = 'none'; - } - - this.payload.role = myRole; - this.userService.changeRole(this.payload); if (this.user.lastCollectionAccessedId !== selectedCollection) { try { diff --git a/client/src/app/pages/admin-processing/admin-processing.component.html b/client/src/app/pages/admin-processing/admin-processing.component.html index 74320110..7dbf7c2b 100644 --- a/client/src/app/pages/admin-processing/admin-processing.component.html +++ b/client/src/app/pages/admin-processing/admin-processing.component.html @@ -27,7 +27,7 @@ - Tenable + Tenable Import diff --git a/client/src/app/pages/admin-processing/collection-processing/collection-processing.component.ts b/client/src/app/pages/admin-processing/collection-processing/collection-processing.component.ts index affd7fb3..c00f84a9 100644 --- a/client/src/app/pages/admin-processing/collection-processing/collection-processing.component.ts +++ b/client/src/app/pages/admin-processing/collection-processing/collection-processing.component.ts @@ -104,7 +104,7 @@ export class CollectionProcessingComponent implements OnInit, OnDestroy { async getCollectionData() { this.collections = null; (await this.collectionService - .getCollections(this.payload.userName)) + .getAllCollections()) .subscribe((result: any) => { this.data = result; this.collections = this.data; diff --git a/client/src/app/pages/admin-processing/collection-processing/collections.service.ts b/client/src/app/pages/admin-processing/collection-processing/collections.service.ts index 1dc1e94f..756fcd83 100644 --- a/client/src/app/pages/admin-processing/collection-processing/collections.service.ts +++ b/client/src/app/pages/admin-processing/collection-processing/collections.service.ts @@ -46,16 +46,15 @@ export class CollectionsService { return new HttpHeaders().set('Authorization', 'Bearer ' + token); } - async getCollections(userName: string) { + async getAllCollections() { const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/collections/${userName}`, { headers }) + return this.http.get(`${this.cpatApiBase}/collections?elevate=true`, { headers }) .pipe(catchError(this.handleError)); } - async getCollectionById(id: string) { - const headers = await this.getAuthHeaders(); - return this.http - .get(`${this.cpatApiBase}/collections/${id}`, { headers }) + async getCollections() { + const headers = await this.getAuthHeaders(); + return this.http.get(`${this.cpatApiBase}/collections`, { headers }) .pipe(catchError(this.handleError)); } @@ -89,13 +88,7 @@ export class CollectionsService { async getUsersForCollection(id: string) { const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/collection/permissions/${+id}`, { headers }) - .pipe(catchError(this.handleError)); - } - - async getPoamApproversByCollectionUser(collectionId: any, userId: any) { - const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/poamApprovers/collection/${+collectionId}/user/${+userId}`, { headers }) + return this.http.get(`${this.cpatApiBase}/permissions/${+id}`, { headers }) .pipe(catchError(this.handleError)); } diff --git a/client/src/app/pages/admin-processing/nessus-plugin-mapping/nessus-plugin-mapping.service.ts b/client/src/app/pages/admin-processing/nessus-plugin-mapping/nessus-plugin-mapping.service.ts index 351d2cd9..6a4b23c5 100644 --- a/client/src/app/pages/admin-processing/nessus-plugin-mapping/nessus-plugin-mapping.service.ts +++ b/client/src/app/pages/admin-processing/nessus-plugin-mapping/nessus-plugin-mapping.service.ts @@ -45,7 +45,7 @@ export class NessusPluginMappingService { getIAVTableData(): Observable { return this.getAuthHeaders().pipe( switchMap(headers => - this.http.get(`${this.cpatApiBase}/iavSummary`, { headers }) + this.http.get(`${this.cpatApiBase}/iav/iavSummary`, { headers }) ), catchError(this.handleError) ); diff --git a/client/src/app/pages/admin-processing/stigmanager-admin/stigmanager-admin.component.html b/client/src/app/pages/admin-processing/stigmanager-admin/stigmanager-admin.component.html index 18ac1c59..21d3713d 100644 --- a/client/src/app/pages/admin-processing/stigmanager-admin/stigmanager-admin.component.html +++ b/client/src/app/pages/admin-processing/stigmanager-admin/stigmanager-admin.component.html @@ -23,7 +23,7 @@
{ const collectionData = { collectionName: collection.name, - description: collection.description || '', + description: collection.description ?? '', collectionOrigin: 'STIG Manager', originCollectionId: +collection.collectionId, }; diff --git a/client/src/app/pages/admin-processing/tenable-admin/tenable-admin.component.html b/client/src/app/pages/admin-processing/tenable-admin/tenable-admin.component.html index 3ab5331a..b1211510 100644 --- a/client/src/app/pages/admin-processing/tenable-admin/tenable-admin.component.html +++ b/client/src/app/pages/admin-processing/tenable-admin/tenable-admin.component.html @@ -23,7 +23,7 @@
{ - (await this.collectionsService.getCollections(currentUser.userName)).subscribe( + (await this.collectionsService.getAllCollections()).subscribe( (response: any) => { this.collectionList = []; response.forEach((collection: { collectionName: any; collectionId: any; }) => { diff --git a/client/src/app/pages/admin-processing/user-processing/users.model.ts b/client/src/app/pages/admin-processing/user-processing/users.model.ts index 2dd6a8f0..82e84dfe 100644 --- a/client/src/app/pages/admin-processing/user-processing/users.model.ts +++ b/client/src/app/pages/admin-processing/user-processing/users.model.ts @@ -8,6 +8,11 @@ !######################################################################## */ +interface Permission { + userId: number; + collectionId: number; + accessLevel: number; +} export interface Users { userId: number; userName: string; @@ -17,11 +22,12 @@ export interface Users { created: string; lastAccess: string; lastCollectionAccessedId: number; - password: string; accountStatus: string; - fullName: string; + fullName: string | null; officeOrg: string; defaultTheme: string; isAdmin: number; lastClaims: any; + points: number; + permissions: Permission[]; } diff --git a/client/src/app/pages/admin-processing/user-processing/users.service.ts b/client/src/app/pages/admin-processing/user-processing/users.service.ts index 7111d1e8..d0dcb5f5 100644 --- a/client/src/app/pages/admin-processing/user-processing/users.service.ts +++ b/client/src/app/pages/admin-processing/user-processing/users.service.ts @@ -9,10 +9,9 @@ */ import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http'; -import { EventEmitter, Injectable, Output } from '@angular/core'; +import { Injectable } from '@angular/core'; import { firstValueFrom, throwError } from 'rxjs'; import { catchError } from 'rxjs/operators'; -import { CollectionsResponse } from './user/user.component'; import { Users } from './users.model'; import { OidcSecurityService } from 'angular-auth-oidc-client'; @@ -20,7 +19,6 @@ import { OidcSecurityService } from 'angular-auth-oidc-client'; providedIn: 'root' }) export class UsersService { - @Output() resetRole: EventEmitter = new EventEmitter(); private cpatApiBase = CPAT.Env.apiBase; constructor( @@ -43,19 +41,10 @@ export class UsersService { return new HttpHeaders().set('Authorization', 'Bearer ' + token); } - - async loginState(state: string) { - const loginState = { loginState: state }; - const headers = await this.getAuthHeaders(); - return this.http - .put(`${this.cpatApiBase}/user/loginState`, loginState, { headers }) - .pipe(catchError(this.handleError)); - } - async getUser(id: any) { const headers = await this.getAuthHeaders(); return this.http - .get(`${this.cpatApiBase}/user/${id}`, { headers }) + .get(`${this.cpatApiBase}/user/${id}?elevate=true`, { headers }) .pipe(catchError(this.handleError)); } @@ -68,41 +57,35 @@ export class UsersService { async getUsers() { const headers = await this.getAuthHeaders(); return this.http - .get(`${this.cpatApiBase}/users`, { headers }) + .get(`${this.cpatApiBase}/users?elevate=true`, { headers }) .pipe(catchError(this.handleError)); } async deletePermission(userId: any, collectionId: any) { const headers = await this.getAuthHeaders(); return this.http - .delete(`${this.cpatApiBase}/permission/${userId}/${collectionId}`, { headers }) + .delete(`${this.cpatApiBase}/permission/${userId}/${collectionId}?elevate=true`, { headers }) .pipe(catchError(this.handleError)); } async postPermission(userPermission: any) { const headers = await this.getAuthHeaders(); return this.http - .post(`${this.cpatApiBase}/permission`, userPermission, { headers }) + .post(`${this.cpatApiBase}/permission?elevate=true`, userPermission, { headers }) .pipe(catchError(this.handleError)); } async updatePermission(userPermission: any) { const headers = await this.getAuthHeaders(); return this.http - .put(`${this.cpatApiBase}/permission`, userPermission, { headers }) - .pipe(catchError(this.handleError)); - } - - async getBasicUserByUserId(userId: any) { - const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/user/basic/${userId}`, { headers }) + .put(`${this.cpatApiBase}/permission?elevate=true`, userPermission, { headers }) .pipe(catchError(this.handleError)); } async updateUser(userData: any) { const headers = await this.getAuthHeaders(); return this.http - .put(`${this.cpatApiBase}/user`, userData, { headers }) + .put(`${this.cpatApiBase}/user?elevate=true`, userData, { headers }) .pipe(catchError(this.handleError)); } @@ -116,24 +99,7 @@ export class UsersService { async updateUserPoints(userPointsData: any) { const headers = await this.getAuthHeaders(); return this.http - .put(`${this.cpatApiBase}/user/updatePoints`, userPointsData, { headers }) + .put(`${this.cpatApiBase}/user/updatePoints?elevate=true`, userPointsData, { headers }) .pipe(catchError(this.handleError)); } - - async getCollection(collectionId: any, userName: string) { - const headers = await this.getAuthHeaders(); - return this.http - .get(`${this.cpatApiBase}/collection/${collectionId}/user/${userName}`, { headers }) - .pipe(catchError(this.handleError)); - } - - async getCollections(userName: string) { - const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/collections/${userName}`, { headers }) - .pipe(catchError(this.handleError)); - } - - changeRole(payload: any) { - this.resetRole.emit(payload); - } } diff --git a/client/src/app/pages/admin-processing/vram-import/vram-import.component.ts b/client/src/app/pages/admin-processing/vram-import/vram-import.component.ts index c2a6f1fa..ffaa012a 100644 --- a/client/src/app/pages/admin-processing/vram-import/vram-import.component.ts +++ b/client/src/app/pages/admin-processing/vram-import/vram-import.component.ts @@ -41,9 +41,7 @@ export class VRAMImportComponent implements OnInit { next: (response: any) => { if (response && response.value) { this.vramUpdatedDate = response.value; - console.log('VRAM Updated Date:', this.vramUpdatedDate); } else { - console.error('Invalid response format:', response); this.vramUpdatedDate = 'N/A'; } }, diff --git a/client/src/app/pages/admin-processing/vram-import/vram-import.service.ts b/client/src/app/pages/admin-processing/vram-import/vram-import.service.ts index df13ede6..35ddd207 100644 --- a/client/src/app/pages/admin-processing/vram-import/vram-import.service.ts +++ b/client/src/app/pages/admin-processing/vram-import/vram-import.service.ts @@ -54,7 +54,7 @@ export class VRAMImportService { async getVramDataUpdatedDate() { const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/vramUpdatedDate`, { headers }) + return this.http.get(`${this.cpatApiBase}/iav/vramUpdatedDate`, { headers }) .pipe(catchError(this.handleError)); } } diff --git a/client/src/app/pages/asset-processing/asset-processing.component.html b/client/src/app/pages/asset-processing/asset-processing.component.html index 96dfdc7a..a39b5fa0 100644 --- a/client/src/app/pages/asset-processing/asset-processing.component.html +++ b/client/src/app/pages/asset-processing/asset-processing.component.html @@ -113,22 +113,24 @@ (onChange)="setAsset($event.value.assetId)" appendTo="body" styleClass="mt-2 w-full" - required> + required + [disabled]="accessLevel < 2">
- + diff --git a/client/src/app/pages/asset-processing/asset-processing.component.scss b/client/src/app/pages/asset-processing/asset-processing.component.scss index a5b386bd..d09b91eb 100644 --- a/client/src/app/pages/asset-processing/asset-processing.component.scss +++ b/client/src/app/pages/asset-processing/asset-processing.component.scss @@ -45,11 +45,8 @@ margin-bottom: 1rem; .right-buttons { + position: relative; display: flex; gap: 0.5rem; } - - .right-buttons { - position: relative; - } } diff --git a/client/src/app/pages/asset-processing/asset-processing.component.ts b/client/src/app/pages/asset-processing/asset-processing.component.ts index fb144dfc..9f70266c 100644 --- a/client/src/app/pages/asset-processing/asset-processing.component.ts +++ b/client/src/app/pages/asset-processing/asset-processing.component.ts @@ -46,6 +46,7 @@ interface AssetEntry { providers: [DialogService] }) export class AssetProcessingComponent implements OnInit, AfterViewInit, OnDestroy { + protected accessLevel: number; @ViewChild('assetLabelsChart') assetLabelsChart!: ElementRef; @ViewChild('assetTable') assetTable!: Table; searchValue: string = ''; @@ -243,30 +244,36 @@ export class AssetProcessingComponent implements OnInit, AfterViewInit, OnDestro async setPayload() { this.user = null; this.payload = null; - - this.subs.sink = (await this.userService.getCurrentUser()).subscribe( - (response: any) => { + this.accessLevel = 0; + (await this.userService.getCurrentUser()).subscribe({ + next: (response: any) => { if (response?.userId) { this.user = response; + const mappedPermissions = this.user.permissions?.map((permission: Permission) => ({ + collectionId: permission.collectionId, + accessLevel: permission.accessLevel, + })); - if (this.user.accountStatus === 'ACTIVE') { this.payload = { ...this.user, - collections: this.user.permissions.map((permission: Permission) => ({ - collectionId: permission.collectionId, - accessLevel: permission.accessLevel, - })) + collections: mappedPermissions }; + if (mappedPermissions.length > 0) { + const selectedPermissions = this.payload.collections.find( + (x: { collectionId: any; }) => x.collectionId == this.payload.lastCollectionAccessedId + ); + + if (selectedPermissions) { + this.accessLevel = selectedPermissions.accessLevel; + } + } this.getAssetData(); } - } else { - console.error('User data is not available or user is not active'); - } - }, - (error) => { + }, + error: (error) => { console.error('An error occurred:', error); } - ); + }); } async getAssetData() { diff --git a/client/src/app/pages/asset-processing/asset/asset.component.html b/client/src/app/pages/asset-processing/asset/asset.component.html index df9035d3..720d5101 100644 --- a/client/src/app/pages/asset-processing/asset/asset.component.html +++ b/client/src/app/pages/asset-processing/asset/asset.component.html @@ -105,6 +105,6 @@

{{invalidDataMessage}}

- +
diff --git a/client/src/app/pages/asset-processing/asset/asset.component.ts b/client/src/app/pages/asset-processing/asset/asset.component.ts index a518cb19..6592318a 100644 --- a/client/src/app/pages/asset-processing/asset/asset.component.ts +++ b/client/src/app/pages/asset-processing/asset/asset.component.ts @@ -9,12 +9,12 @@ */ import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges } from '@angular/core'; -import { Router } from '@angular/router'; import { ConfirmationService, MessageService } from 'primeng/api'; import { Subscription } from 'rxjs'; import { SubSink } from 'subsink'; import { SharedService } from '../../../common/services/shared.service'; import { AssetService } from '../assets.service'; +import { CollectionsService } from '../../admin-processing/collection-processing/collections.service'; @Component({ selector: 'cpat-asset', @@ -44,7 +44,7 @@ export class AssetComponent implements OnInit, OnChanges, OnDestroy { constructor( private assetService: AssetService, - private router: Router, + private collectionService: CollectionsService, private sharedService: SharedService, private confirmationService: ConfirmationService, private messageService: MessageService @@ -84,12 +84,7 @@ export class AssetComponent implements OnInit, OnChanges, OnDestroy { } async getCollectionData() { - const userName = this.payload?.userName; - if (!userName) { - console.error('UserName is not available'); - return; - } - this.subs.sink = (await this.assetService.getCollections(userName)).subscribe((collections: any) => { + this.subs.sink = (await this.collectionService.getCollections()).subscribe((collections: any) => { this.collectionList = collections || []; this.collectionOptions = this.transformToDropdownOptions(collections, 'collectionName', 'collectionId'); if (this.asset.collectionId) { diff --git a/client/src/app/pages/asset-processing/assets.service.ts b/client/src/app/pages/asset-processing/assets.service.ts index 7aa88437..43028194 100644 --- a/client/src/app/pages/asset-processing/assets.service.ts +++ b/client/src/app/pages/asset-processing/assets.service.ts @@ -56,18 +56,6 @@ export class AssetService { .pipe(catchError(this.handleError)); } - async getCollection(collectionId: any, userName: string) { - const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/collection/${collectionId}/user/${userName}`, { headers }) - .pipe(catchError(this.handleError)); - } - - async getCollections(userName: string) { - const url = `${this.cpatApiBase}/collections/${userName}`; - const headers = await this.getAuthHeaders(); - return this.http.get(url, { headers }).pipe(catchError(this.handleError)); - } - async getCollectionAssetLabel(id: string) { const headers = await this.getAuthHeaders(); return this.http.get(`${this.cpatApiBase}/metrics/collection/${id}/assetlabel`, { headers }) diff --git a/client/src/app/pages/import-processing/stigmanager-import/stigManagerAssetsTable/stigManagerAssetsTable.component.scss b/client/src/app/pages/import-processing/stigmanager-import/stigManagerAssetsTable/stigManagerAssetsTable.component.scss index fa6c0efd..5ce8269b 100644 --- a/client/src/app/pages/import-processing/stigmanager-import/stigManagerAssetsTable/stigManagerAssetsTable.component.scss +++ b/client/src/app/pages/import-processing/stigmanager-import/stigManagerAssetsTable/stigManagerAssetsTable.component.scss @@ -62,11 +62,8 @@ margin-bottom: 1rem; .right-buttons { + position: relative; display: flex; gap: 0.5rem; } - - .right-buttons { - position: relative; - } } diff --git a/client/src/app/pages/import-processing/stigmanager-import/stigManagerPoamAssetsTable/stigManagerPoamAssetsTable.component.scss b/client/src/app/pages/import-processing/stigmanager-import/stigManagerPoamAssetsTable/stigManagerPoamAssetsTable.component.scss index fa6c0efd..5ce8269b 100644 --- a/client/src/app/pages/import-processing/stigmanager-import/stigManagerPoamAssetsTable/stigManagerPoamAssetsTable.component.scss +++ b/client/src/app/pages/import-processing/stigmanager-import/stigManagerPoamAssetsTable/stigManagerPoamAssetsTable.component.scss @@ -62,11 +62,8 @@ margin-bottom: 1rem; .right-buttons { + position: relative; display: flex; gap: 0.5rem; } - - .right-buttons { - position: relative; - } } diff --git a/client/src/app/pages/import-processing/stigmanager-import/stigmanager-import.component.ts b/client/src/app/pages/import-processing/stigmanager-import/stigmanager-import.component.ts index 4409be87..6d8eae1f 100644 --- a/client/src/app/pages/import-processing/stigmanager-import/stigmanager-import.component.ts +++ b/client/src/app/pages/import-processing/stigmanager-import/stigmanager-import.component.ts @@ -293,7 +293,7 @@ export class STIGManagerImportComponent implements OnInit, OnDestroy { 'Rule Title': item.rules[0].title, 'ruleId': item.rules[0].ruleId, 'Benchmark ID': item.stigs[0].benchmarkId, - 'Severity': item.severity === 'high' ? 'CAT I - Critical/High' : + 'Severity': item.severity === 'high' ? 'CAT I - High' : item.severity === 'medium' ? 'CAT II - Medium' : item.severity === 'low' ? 'CAT III - Low' : item.severity, 'Asset Count': item.assetCount @@ -312,7 +312,7 @@ export class STIGManagerImportComponent implements OnInit, OnDestroy { this.findingsCount = this.treeData.length; const severityGroups = data.reduce((groups: any, item: any) => { - const severity = item.severity === 'high' ? 'CAT I - Critical/High' : + const severity = item.severity === 'high' ? 'CAT I - High' : item.severity === 'medium' ? 'CAT II - Medium' : item.severity === 'low' ? 'CAT III - Low' : item.severity; if (!groups[severity]) { @@ -327,7 +327,7 @@ export class STIGManagerImportComponent implements OnInit, OnDestroy { severityCount: count })); - const allSeverities = ['CAT I - Critical/High', 'CAT II - Medium', 'CAT III - Low']; + const allSeverities = ['CAT I - High', 'CAT II - Medium', 'CAT III - Low']; allSeverities.forEach(severity => { if (!findings.find(finding => finding.severity === severity)) { findings.push({ severity, severityCount: 0 }); @@ -393,7 +393,7 @@ export class STIGManagerImportComponent implements OnInit, OnDestroy { severityCount: count })); - const allSeverities = ['CAT I - Critical/High', 'CAT II - Medium', 'CAT III - Low']; + const allSeverities = ['CAT I - High', 'CAT II - Medium', 'CAT III - Low']; allSeverities.forEach(severity => { if (!findings.find(finding => finding.severity === severity)) { findings.push({ severity, severityCount: 0 }); diff --git a/client/src/app/pages/label-processing/label-processing.component.html b/client/src/app/pages/label-processing/label-processing.component.html index 2cec69a8..49ec00e7 100644 --- a/client/src/app/pages/label-processing/label-processing.component.html +++ b/client/src/app/pages/label-processing/label-processing.component.html @@ -75,16 +75,18 @@ (onChange)="setLabel($event.value.labelId)" styleClass="mt-4 w-full" required - appendTo="body"> + appendTo="body" + [disabled]="accessLevel < 2">
diff --git a/client/src/app/pages/label-processing/label-processing.component.ts b/client/src/app/pages/label-processing/label-processing.component.ts index 604670a7..d18a1f8d 100644 --- a/client/src/app/pages/label-processing/label-processing.component.ts +++ b/client/src/app/pages/label-processing/label-processing.component.ts @@ -36,6 +36,7 @@ interface LabelEntry { providers: [DialogService] }) export class LabelProcessingComponent implements OnInit, OnDestroy { + protected accessLevel: number; @ViewChild('labelPopup') labelPopup!: TemplateRef; @ViewChild('labelTable') labelTable!: Table; labelDialogVisible: boolean = false; @@ -44,7 +45,6 @@ export class LabelProcessingComponent implements OnInit, OnDestroy { allColumns = [this.customColumn, ...this.defaultColumns]; data: LabelEntry[] = []; filterValue: string = ''; - users: any; user: any; public isLoggedIn = false; @@ -82,30 +82,36 @@ export class LabelProcessingComponent implements OnInit, OnDestroy { async setPayload() { this.user = null; this.payload = null; - - this.subs.sink = (await this.userService.getCurrentUser()).subscribe( - (response: any) => { + this.accessLevel = 0; + (await this.userService.getCurrentUser()).subscribe({ + next: (response: any) => { if (response?.userId) { this.user = response; - if (this.user.accountStatus === 'ACTIVE') { - this.payload = { - ...this.user, - collections: this.user.permissions.map((permission: Permission) => ({ - collectionId: permission.collectionId, - accessLevel: permission.accessLevel, - })) - }; - - this.getLabelData(); + const mappedPermissions = this.user.permissions?.map((permission: Permission) => ({ + collectionId: permission.collectionId, + accessLevel: permission.accessLevel, + })); + + this.payload = { + ...this.user, + collections: mappedPermissions + }; + if (mappedPermissions.length > 0) { + const selectedPermissions = this.payload.collections.find( + (x: { collectionId: any; }) => x.collectionId == this.payload.lastCollectionAccessedId + ); + + if (selectedPermissions) { + this.accessLevel = selectedPermissions.accessLevel; + } } - } else { - console.error('User data is not available or user is not active'); + this.getLabelData(); } }, - (error) => { + error: (error) => { console.error('An error occurred:', error); } - ); + }); } async getLabelData() { diff --git a/client/src/app/pages/marketplace/marketplace.service.ts b/client/src/app/pages/marketplace/marketplace.service.ts index b9cf140c..c239bfc8 100644 --- a/client/src/app/pages/marketplace/marketplace.service.ts +++ b/client/src/app/pages/marketplace/marketplace.service.ts @@ -58,14 +58,14 @@ export class MarketplaceService { async getUserThemes(userId: number) { const headers = await this.getAuthHeaders(); return this.http - .get(`${this.cpatApiBase}/marketplace/user-themes/${userId}`, { headers }) + .get(`${this.cpatApiBase}/marketplace/user-themes`, { headers }) .pipe(catchError(this.handleError)); } async getUserPoints(userId: number) { const headers = await this.getAuthHeaders(); return this.http - .get(`${this.cpatApiBase}/marketplace/user-points/${userId}`, { headers }) + .get(`${this.cpatApiBase}/marketplace/user-points`, { headers }) .pipe(catchError(this.handleError)); } } diff --git a/client/src/app/pages/poam-processing/poam-approve/poam-approve.component.html b/client/src/app/pages/poam-processing/poam-approve/poam-approve.component.html index 81af9e1d..c66ec60e 100644 --- a/client/src/app/pages/poam-processing/poam-approve/poam-approve.component.html +++ b/client/src/app/pages/poam-processing/poam-approve/poam-approve.component.html @@ -13,23 +13,23 @@
- +
- +
- +
diff --git a/client/src/app/pages/poam-processing/poam-approve/poam-approve.component.ts b/client/src/app/pages/poam-processing/poam-approve/poam-approve.component.ts index 00578a1b..617c5f25 100644 --- a/client/src/app/pages/poam-processing/poam-approve/poam-approve.component.ts +++ b/client/src/app/pages/poam-processing/poam-approve/poam-approve.component.ts @@ -8,6 +8,12 @@ !######################################################################## */ +interface Permission { + userId: number; + collectionId: number; + accessLevel: number; +} + import { AfterViewInit, Component, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core'; import { SubSink } from 'subsink'; import { ActivatedRoute, Router } from '@angular/router'; @@ -31,6 +37,7 @@ import { MessageService } from 'primeng/api'; ] }) export class PoamApproveComponent implements OnInit, AfterViewInit, OnDestroy { + protected accessLevel: number; private subs = new SubSink(); public isLoggedIn = false; hqsChecked: boolean = false; @@ -42,6 +49,7 @@ export class PoamApproveComponent implements OnInit, AfterViewInit, OnDestroy { comments: any; selectedCollection: any; user: any; + payload: any; private subscriptions = new Subscription(); approvalStatusOptions = [ { label: 'Not Reviewed', value: 'Not Reviewed' }, @@ -79,19 +87,38 @@ export class PoamApproveComponent implements OnInit, AfterViewInit, OnDestroy { async setPayload() { this.user = null; + this.payload = null; + this.accessLevel = 0; (await this.userService.getCurrentUser()).subscribe({ next: (response: any) => { if (response?.userId) { this.user = response; + const mappedPermissions = this.user.permissions?.map((permission: Permission) => ({ + collectionId: permission.collectionId, + accessLevel: permission.accessLevel, + })); + + this.payload = { + ...this.user, + collections: mappedPermissions + }; + if (mappedPermissions.length > 0) { + const selectedPermissions = this.payload.collections.find( + (x: { collectionId: any; }) => x.collectionId == this.payload.lastCollectionAccessedId + ); + + if (selectedPermissions) { + this.accessLevel = selectedPermissions.accessLevel; + } + } this.getData(); - } else { - console.error('User data is not available or user is not active'); } }, error: (error) => { console.error('An error occurred:', error); } }); + this.cdr.detectChanges(); } async getData() { @@ -154,6 +181,7 @@ export class PoamApproveComponent implements OnInit, AfterViewInit, OnDestroy { (await this.poamApproveService.updatePoamApprover(approvalData)).subscribe( () => { this.messageService.add({ severity: 'success', summary: 'Success', detail: 'Approval saved successfully.' }); + this.router.navigateByUrl(`/poam-processing/poam-details/${this.poamId}`); }, (error) => { this.messageService.add({ severity: 'warn', summary: 'Information', detail: 'Failed to update POAM Approval. Please validate data entry and try again.' }); diff --git a/client/src/app/pages/poam-processing/poam-components/poam-assigned-grid/poam-assigned-grid.component.html b/client/src/app/pages/poam-processing/poam-components/poam-assigned-grid/poam-assigned-grid.component.html index 4cdcc5a5..0831bab2 100644 --- a/client/src/app/pages/poam-processing/poam-components/poam-assigned-grid/poam-assigned-grid.component.html +++ b/client/src/app/pages/poam-processing/poam-components/poam-assigned-grid/poam-assigned-grid.component.html @@ -29,9 +29,9 @@ - {{ row[getColumnKey(col)] }} - - + {{ row[getColumnKey(col)] }} + + diff --git a/client/src/app/pages/poam-processing/poam-components/poam-grid/poam-grid.component.html b/client/src/app/pages/poam-processing/poam-components/poam-grid/poam-grid.component.html index 4dd76b88..2a6a11e4 100644 --- a/client/src/app/pages/poam-processing/poam-components/poam-grid/poam-grid.component.html +++ b/client/src/app/pages/poam-processing/poam-components/poam-grid/poam-grid.component.html @@ -27,7 +27,7 @@ scrollHeight="50rem" [paginator]="false" columnResizeMode="fit" - [globalFilterFields]="['lastUpdated', 'poamId', 'status', 'adjSeverity', 'submitter', 'submittedDate', 'scheduledCompletionDate']"> + [globalFilterFields]="['lastUpdated', 'poamId', 'status', 'stigBenchmarkId', 'adjSeverity', 'submitter', 'submittedDate', 'scheduledCompletionDate']"> @@ -54,6 +54,12 @@ + +
+ STIG Benchmark + +
+
Adjusted Severity @@ -79,7 +85,7 @@
- Manage + POAM
@@ -89,12 +95,13 @@ {{ row.poamId }} {{ row.status }} {{ row.source }} + {{ row.stigBenchmarkId }} {{ row.adjSeverity }} {{ row.submitter }} {{ row.submittedDate }} {{ row.scheduledCompletionDate }} - +
diff --git a/client/src/app/pages/poam-processing/poam-components/poam-grid/poam-grid.component.ts b/client/src/app/pages/poam-processing/poam-components/poam-grid/poam-grid.component.ts index 7ca38026..de013485 100644 --- a/client/src/app/pages/poam-processing/poam-components/poam-grid/poam-grid.component.ts +++ b/client/src/app/pages/poam-processing/poam-components/poam-grid/poam-grid.component.ts @@ -71,6 +71,7 @@ export class PoamGridComponent implements OnChanges { poamId: poam.poamId, status: poam.status, source: poam.vulnerabilitySource, + stigBenchmarkId: poam.stigBenchmarkId ?? '', adjSeverity: poam.adjSeverity, submitter: poam.submitterName, submittedDate: poam.submittedDate?.split('T')[0], diff --git a/client/src/app/pages/poam-processing/poam-components/poam-mainchart/poam-mainchart.component.html b/client/src/app/pages/poam-processing/poam-components/poam-mainchart/poam-mainchart.component.html index aee7b839..13036bbf 100644 --- a/client/src/app/pages/poam-processing/poam-components/poam-mainchart/poam-mainchart.component.html +++ b/client/src/app/pages/poam-processing/poam-components/poam-mainchart/poam-mainchart.component.html @@ -125,7 +125,7 @@
- +
diff --git a/client/src/app/pages/poam-processing/poam-components/poam-mainchart/poam-mainchart.component.ts b/client/src/app/pages/poam-processing/poam-components/poam-mainchart/poam-mainchart.component.ts index 40e3cd63..040328d4 100644 --- a/client/src/app/pages/poam-processing/poam-components/poam-mainchart/poam-mainchart.component.ts +++ b/client/src/app/pages/poam-processing/poam-components/poam-mainchart/poam-mainchart.component.ts @@ -15,6 +15,13 @@ import { addDays, differenceInCalendarDays } from 'date-fns'; import { DropdownChangeEvent } from 'primeng/dropdown'; import { MultiSelectChangeEvent } from 'primeng/multiselect'; import ChartDataLabels from 'chartjs-plugin-datalabels'; +import { UsersService } from '../../../admin-processing/user-processing/users.service'; + +interface Permission { + userId: number; + collectionId: number; + accessLevel: number; +} @Component({ selector: 'cpat-poam-mainchart', @@ -22,6 +29,7 @@ import ChartDataLabels from 'chartjs-plugin-datalabels'; styleUrls: ['./poam-mainchart.component.scss'] }) export class PoamMainchartComponent implements OnInit, OnChanges, AfterViewInit { + protected accessLevel: number; @Output() poamsChange = new EventEmitter(); @Input() poams!: any[]; @Input() showAddButton: boolean = false; @@ -31,7 +39,8 @@ export class PoamMainchartComponent implements OnInit, OnChanges, AfterViewInit @ViewChild('poamLabelChart') poamLabelChart!: ElementRef; @ViewChild('poamSeverityChart') poamSeverityChart!: ElementRef; @ViewChild('poamScheduledCompletionChart') poamScheduledCompletionChart!: ElementRef; - + user: any; + payload: any; poamsForChart: any[] = []; public poamLabel: any[] = []; public selectedStatus: any = null; @@ -58,9 +67,11 @@ export class PoamMainchartComponent implements OnInit, OnChanges, AfterViewInit ]; poamSeverities = [ - { value: 'CAT I - Critical/High', label: 'CAT I - Critical/High' }, + { value: 'CAT I - Critical', label: 'CAT I - Critical' }, + { value: 'CAT I - High', label: 'CAT I - High' }, { value: 'CAT II - Medium', label: 'CAT II - Medium' }, - { value: 'CAT III - Low', label: 'CAT III - Low' } + { value: 'CAT III - Low', label: 'CAT III - Low' }, + { value: 'CAT III - Informational', label: 'CAT III - Informational' } ]; poamScheduledCompletions = [ @@ -157,13 +168,15 @@ export class PoamMainchartComponent implements OnInit, OnChanges, AfterViewInit constructor( private router: Router, private cdr: ChangeDetectorRef, - private renderer: Renderer2 + private renderer: Renderer2, + private userService: UsersService ) { Chart.register(...registerables); } - ngOnInit() { + async ngOnInit() { if (this.poams) { + await this.setPayload(); this.initializePoamLabel(); this.initializeChart(); } @@ -190,6 +203,41 @@ export class PoamMainchartComponent implements OnInit, OnChanges, AfterViewInit this.applyCanvasStyles(); } + async setPayload() { + this.user = null; + this.payload = null; + this.accessLevel = 0; + (await this.userService.getCurrentUser()).subscribe({ + next: (response: any) => { + if (response?.userId) { + this.user = response; + const mappedPermissions = this.user.permissions?.map((permission: Permission) => ({ + collectionId: permission.collectionId, + accessLevel: permission.accessLevel, + })); + + this.payload = { + ...this.user, + collections: mappedPermissions + }; + if (mappedPermissions.length > 0) { + const selectedPermissions = this.payload.collections.find( + (x: { collectionId: any; }) => x.collectionId == this.payload.lastCollectionAccessedId + ); + + if (selectedPermissions) { + this.accessLevel = selectedPermissions.accessLevel; + } + } + } + }, + error: (error) => { + console.error('An error occurred:', error); + } + }); + this.cdr.detectChanges(); + } + private applyCanvasStyles(): void { if (this.poamStatusChart?.nativeElement) { this.renderer.setStyle(this.poamStatusChart.nativeElement, 'height', this.canvasHeight); diff --git a/client/src/app/pages/poam-processing/poam-details/poam-details.component.html b/client/src/app/pages/poam-processing/poam-details/poam-details.component.html index 52914d6f..e83c0735 100644 --- a/client/src/app/pages/poam-processing/poam-details/poam-details.component.html +++ b/client/src/app/pages/poam-processing/poam-details/poam-details.component.html @@ -21,9 +21,9 @@
- - - + + +
@@ -51,8 +51,8 @@
- - + + {{option.label}} @@ -61,17 +61,18 @@
- - + +
- +
- -
- - -
-
- - + +
- - + +
-
- - +
+ +
- - + +
- +
- - + +
- - + +
- - -
- - -
- - + +
- - + +
- +
@@ -168,7 +157,7 @@ Members Assigned - + @@ -176,10 +165,10 @@ {{getAssigneeName(assignee.userId)}} - + - + @@ -210,7 +199,7 @@ Approved Date Comments - + @@ -218,16 +207,16 @@ {{getApproverName(approver.userId)}} - + {{approver.approvalStatus}} {{approver.approvedDate ? (approver.approvedDate | date:'yyyy-MM-dd') : 'Not Reviewed'}} {{approver.comments}} - + - + @@ -256,7 +245,7 @@ Asset - + @@ -264,10 +253,10 @@ {{getAssetName(asset.assetId)}} - + - + @@ -298,7 +287,8 @@
- + +
@@ -313,7 +303,8 @@
- + +
@@ -328,7 +319,8 @@
- + +
@@ -344,28 +336,28 @@
- - + +
- - + +
- - + +
- - + +
- - + +
@@ -381,6 +373,7 @@
+ @@ -388,9 +381,9 @@ Milestone Date Milestone Status Milestone Team - - - + + + @@ -398,7 +391,7 @@ - + {{milestone.milestoneComments}} @@ -408,7 +401,7 @@ - + {{milestone.milestoneDate | date:'yyyy-MM-dd'}} @@ -418,7 +411,7 @@ - + {{milestone.milestoneStatus}} @@ -428,7 +421,7 @@ - + {{milestone.milestoneTeam}} @@ -437,12 +430,12 @@ - - - - - - + + + + + + @@ -471,7 +464,7 @@ Label - + @@ -479,10 +472,10 @@ {{label.labelName}} - + - + @@ -505,8 +498,8 @@ - - + +
diff --git a/client/src/app/pages/poam-processing/poam-details/poam-details.component.ts b/client/src/app/pages/poam-processing/poam-details/poam-details.component.ts index 0fd39bf4..3e21f3b5 100644 --- a/client/src/app/pages/poam-processing/poam-details/poam-details.component.ts +++ b/client/src/app/pages/poam-processing/poam-details/poam-details.component.ts @@ -12,8 +12,8 @@ import { DatePipe } from '@angular/common'; import { ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { addDays, format, isAfter } from 'date-fns'; -import { Subscription, forkJoin, of, throwError } from 'rxjs'; -import { catchError, map, switchMap, tap } from 'rxjs/operators'; +import { Subscription, forkJoin, of } from 'rxjs'; +import { catchError } from 'rxjs/operators'; import { SubSink } from 'subsink'; import { SharedService } from '../../../common/services/shared.service'; import { CollectionsService } from '../../admin-processing/collection-processing/collections.service'; @@ -25,20 +25,7 @@ import { ConfirmationService, MessageService } from 'primeng/api'; import { Table } from 'primeng/table'; import { jsonToPlainText } from "json-to-plain-text"; import { AAPackageService } from '../../admin-processing/aaPackage-processing/aaPackage-processing.service'; -function getRoleFromAccessLevel(accessLevel: number): string { - switch (accessLevel) { - case 1: - return 'viewer'; - case 2: - return 'submitter'; - case 3: - return 'approver'; - case 4: - return 'cat1approver'; - default: - return 'none'; - } -} + interface AAPackage { aaPackageId: number; aaPackage: string; @@ -61,36 +48,19 @@ interface Permission { }) export class PoamDetailsComponent implements OnInit, OnDestroy { @ViewChild('dt') table: Table; - editingRows: { [s: string]: boolean } = {}; - clonedAssignees: { [s: string]: any; } = {}; + protected accessLevel: number; clonedMilestones: { [s: string]: any; } = {}; - milestoneStatusOptions = [ - { label: 'Pending', value: 'Pending' }, - { label: 'Complete', value: 'Complete' } - ]; - - approvalStatusOptions = [ - { label: 'Not Reviewed', value: 'Not Reviewed' }, - { label: 'Approved', value: 'Approved' }, - { label: 'Rejected', value: 'Rejected' } - ]; - poamLabels: any[] = []; labelList: any[] = []; - newLabel: any = { labelId: null }; errorDialogVisible: boolean = false; errorMessage: string = ''; errorHeader: string = 'Error'; - public isLoggedIn = false; - users: any; user: any; poam: any; poamId: string = ""; payload: any; dates: any = {}; collectionUsers: any; - collectionSubmitters: any[] = []; - collection: any; collectionApprovers: any; collectionBasicList: any[] = []; aaPackages: AAPackage[] = []; @@ -99,27 +69,29 @@ export class PoamDetailsComponent implements OnInit, OnDestroy { poamMilestones: any[] = []; pluginData: any; assets: any; + assetList: any[] = []; poamAssets: any[] = []; poamAssignees: any[] = []; isEmassCollection: boolean = false; - canModifySubmitter: boolean = false; - showApprove: boolean = false; - showSubmit: boolean = false; - showClose: boolean = false; showCheckData: boolean = false; - stigmanCollections: any[] = []; stigmanSTIGs: any; tenableVulnResponse: any; tenablePluginData: string; filteredStigmanSTIGs: string[] = []; - selectedStig: any = null; + filteredVulnerabilitySources: string[] = []; selectedStigTitle: string = ''; selectedStigObject: any = null; selectedStigBenchmarkId: string = ''; stigmanCollectionId: any; - groupId: any; - assetList: any[] = []; stateData: any; + selectedCollection: any; + private subscriptions = new Subscription(); + private subs = new SubSink(); + + milestoneStatusOptions = [ + { label: 'Pending', value: 'Pending' }, + { label: 'Complete', value: 'Complete' } + ]; vulnerabilitySources: string[] = [ "ACAS Nessus Scanner", @@ -127,30 +99,27 @@ export class PoamDetailsComponent implements OnInit, OnDestroy { "RMF Controls", "Task Order", ]; - filteredVulnerabilitySources: string[] =[]; + statusOptions = [ { label: 'Draft', value: 'Draft', disabled: false }, { label: 'Closed', value: 'Closed', disabled: false }, { label: 'Expired', value: 'Expired', disabled: false }, { label: 'Submitted', value: 'Submitted', disabled: true }, { label: 'Approved', value: 'Approved', disabled: true }, + { label: 'Pending CAT-I Approval', value: 'Pending CAT-I Approval', disabled: true }, { label: 'Rejected', value: 'Rejected', disabled: true }, { label: 'Extension Requested', value: 'Extension Requested', disabled: true }, ]; - rawSeverityOptions = [ - { label: 'CAT I - Critical/High', value: 'CAT I - Critical/High' }, - { label: 'CAT II - Medium', value: 'CAT II - Medium' }, - { label: 'CAT III - Low', value: 'CAT III - Low' }, - ]; - - adjSeverityOptions = [ - { label: 'CAT I - Critical/High', value: 'CAT I - Critical/High' }, - { label: 'CAT II - Medium', value: 'CAT II - Medium' }, - { label: 'CAT III - Low', value: 'CAT III - Low' }, + severityOptions = [ + { value: 'CAT I - Critical', label: 'CAT I - Critical' }, + { value: 'CAT I - High', label: 'CAT I - High' }, + { value: 'CAT II - Medium', label: 'CAT II - Medium' }, + { value: 'CAT III - Low', label: 'CAT III - Low' }, + { value: 'CAT III - Informational', label: 'CAT III - Informational' } ]; - residualRiskOptions = [ + ratingOptions = [ { label: 'Very Low', value: 'Very Low' }, { label: 'Low', value: 'Low' }, { label: 'Moderate', value: 'Moderate' }, @@ -158,47 +127,6 @@ export class PoamDetailsComponent implements OnInit, OnDestroy { { label: 'Very High', value: 'Very High' }, ]; - likelihoodOptions = [ - { label: 'Very Low', value: 'Very Low' }, - { label: 'Low', value: 'Low' }, - { label: 'Moderate', value: 'Moderate' }, - { label: 'High', value: 'High' }, - { label: 'Very High', value: 'Very High' }, - ]; - - relevanceOfThreatOptions = [ - { label: 'Very Low', value: 'Very Low' }, - { label: 'Low', value: 'Low' }, - { label: 'Moderate', value: 'Moderate' }, - { label: 'High', value: 'High' }, - { label: 'Very High', value: 'Very High' }, - ]; - - businessImpactRatingOptions = [ - { label: 'Very Low', value: 'Very Low' }, - { label: 'Low', value: 'Low' }, - { label: 'Moderate', value: 'Moderate' }, - { label: 'High', value: 'High' }, - { label: 'Very High', value: 'Very High' }, - ]; - - steps = [ - { label: 'Assignees' }, - { label: 'Approvers' }, - { label: 'Assets' }, - { label: 'Predisposing Conditions' }, - { label: 'Mitigations' }, - { label: 'Required Resources' }, - { label: 'Residual Risk' }, - { label: 'Business Impact' }, - { label: 'Milestones' }, - { label: 'POAM Labels' }, - { label: 'Notes' }, - ]; - selectedCollection: any; - private subscriptions = new Subscription(); - private subs = new SubSink(); - constructor( private aaPackageService: AAPackageService, private confirmationService: ConfirmationService, @@ -232,45 +160,30 @@ export class PoamDetailsComponent implements OnInit, OnDestroy { async setPayload() { this.user = null; this.payload = null; - + this.accessLevel = 0; (await this.userService.getCurrentUser()).subscribe({ next: (response: any) => { if (response?.userId) { this.user = response; - if (this.user.accountStatus === 'ACTIVE') { - - const mappedPermissions = this.user.permissions.map((permission: Permission) => ({ - collectionId: permission.collectionId, - accessLevel: permission.accessLevel, - })); + const mappedPermissions = this.user.permissions?.map((permission: Permission) => ({ + collectionId: permission.collectionId, + accessLevel: permission.accessLevel, + })); - this.payload = { - ...this.user, - collections: mappedPermissions - }; - - const selectedPermissions = this.payload.collections.find((x: { collectionId: any; }) => x.collectionId == this.payload.lastCollectionAccessedId); - let myRole: string; - - if (!selectedPermissions && !this.user.isAdmin) { - myRole = 'none'; - } else if (this.user.isAdmin) { - myRole = 'admin'; - } else if (selectedPermissions) { - myRole = getRoleFromAccessLevel(selectedPermissions.accessLevel); - } else { - myRole = 'none'; + this.payload = { + ...this.user, + collections: mappedPermissions + }; + if (mappedPermissions.length > 0) { + const selectedPermissions = this.payload.collections.find( + (x: { collectionId: any; }) => x.collectionId == this.payload.lastCollectionAccessedId + ); + + if (selectedPermissions) { + this.accessLevel = selectedPermissions.accessLevel; } - - this.payload.role = myRole; - this.showApprove = ['admin', 'cat1approver', 'approver'].includes(this.payload.role); - this.showClose = ['admin', 'cat1approver', 'approver', 'submitter'].includes(this.payload.role); - this.showSubmit = ['admin', 'cat1approver', 'approver', 'submitter'].includes(this.payload.role); - this.canModifySubmitter = ['admin', 'cat1approver', 'approver', 'submitter'].includes(this.payload.role); - this.getData(); } - } else { - console.error('User data is not available or user is not active'); + this.getData(); } }, error: (error) => { @@ -295,22 +208,21 @@ export class PoamDetailsComponent implements OnInit, OnDestroy { } else { forkJoin([ await this.poamService.getPoam(this.poamId), - await this.poamService.getCollection(this.payload.lastCollectionAccessedId, this.payload.userName), await this.collectionService.getUsersForCollection(this.payload.lastCollectionAccessedId), await this.assetService.getAssetsByCollection(this.payload.lastCollectionAccessedId), await this.poamService.getPoamAssignees(this.poamId), await this.poamService.getPoamApprovers(this.poamId), await this.poamService.getPoamMilestones(this.poamId), await this.poamService.getPoamLabelsByPoam(this.poamId) - ]).subscribe(([poam, collection, users, collectionAssets, assignees, poamApprovers, poamMilestones, poamLabels]: any) => { + ]).subscribe(([poam, users, collectionAssets, assignees, poamApprovers, poamMilestones, poamLabels]: any) => { this.poam = { ...poam, hqs: poam.hqs === 1 ? true : false }; this.dates.scheduledCompletionDate = poam.scheduledCompletionDate ? new Date(poam.scheduledCompletionDate) : null; this.dates.iavComplyByDate = poam.iavComplyByDate ? new Date(poam.iavComplyByDate) : null; this.dates.submittedDate = poam.submittedDate ? new Date(poam.submittedDate) : null; this.dates.closedDate = poam.closedDate ? new Date(poam.closedDate) : null; - this.collection = collection.collection; this.collectionUsers = users; this.assets = collectionAssets; + this.poamAssets = []; this.poamAssignees = assignees; this.poamApprovers = poamApprovers; this.poamMilestones = poamMilestones.poamMilestones.map((milestone: any) => ({ @@ -319,32 +231,19 @@ export class PoamDetailsComponent implements OnInit, OnDestroy { })); this.selectedStigTitle = this.poam.stigTitle; this.selectedStigBenchmarkId = this.poam.stigBenchmarkId; - this.collectionApprovers = this.collectionUsers.filter((user: Permission) => user.accessLevel >= 3 || this.user.isAdmin); + this.collectionApprovers = this.collectionUsers.filter((user: Permission) => user.accessLevel >= 3); if (this.collectionApprovers.length > 0 && (this.poamApprovers == undefined || this.poamApprovers.length == 0)) { this.addDefaultApprovers(); } - if (this.collectionUsers) { - this.collectionUsers.forEach((user: any) => { - if (user.accessLevel >= 2) { - this.collectionSubmitters.push({ ...user }); - } - }); - } + if (this.poam.tenablePluginData) { - try { - const pluginDataObject = JSON.parse(this.poam.tenablePluginData); - this.tenablePluginData = jsonToPlainText(pluginDataObject, {}); - } catch (error) { - this.tenablePluginData = this.poam.tenablePluginData; - } - } - if ((this.poam.vulnerabilitySource === 'STIG' || this.poam.vulnerabilitySource === 'ACAS Nessus Scanner') && !this.isEmassCollection) { - this.poamAssets = []; - } else { - this.fetchAssets(); + this.parsePluginData(this.poam.tenablePluginData); } + + if (this.isEmassCollection) { + this.fetchAssets(); + } this.poamLabels = poamLabels; - this.setChartSelectionData(); }); (await this.sharedService.getSTIGsFromSTIGMAN()).subscribe({ next: (data) => { @@ -362,14 +261,12 @@ export class PoamDetailsComponent implements OnInit, OnDestroy { } async createNewACASPoam() { - this.canModifySubmitter = true; this.pluginData = this.stateData.pluginData; await this.loadVulnerabilitiy(this.pluginData.id); forkJoin([ - await this.poamService.getCollection(this.payload.lastCollectionAccessedId, this.payload.userName), await this.collectionService.getUsersForCollection(this.payload.lastCollectionAccessedId), await this.assetService.getAssetsByCollection(this.payload.lastCollectionAccessedId), - ]).subscribe(async ([collection, users, collectionAssets]: any) => { + ]).subscribe(async ([users, collectionAssets]: any) => { this.poam = { poamId: "ADDPOAM", collectionId: this.payload.lastCollectionAccessedId, @@ -389,58 +286,60 @@ ${this.pluginData.description}` || "", scheduledCompletionDate: '', submitterId: this.payload.userId, status: "Draft", - tenablePluginData: this.pluginData || "", + tenablePluginData: this.pluginData ? JSON.stringify(this.pluginData) : "", hqs: false, }; this.dates.scheduledCompletionDate = null; this.dates.iavComplyByDate = this.poam.iavComplyByDate ? new Date(this.poam.iavComplyByDate) : null; this.dates.submittedDate = new Date(this.poam.submittedDate); - - this.collection = collection; this.collectionUsers = users; this.assets = collectionAssets; this.poamAssets = []; this.poamAssignees = []; this.collectionApprovers = []; - this.collectionApprovers = this.collectionUsers.filter((user: Permission) => user.accessLevel >= 3 || this.user.isAdmin); + this.collectionApprovers = this.collectionUsers.filter((user: Permission) => user.accessLevel >= 3); this.poamApprovers = this.collectionApprovers.map((approver: any) => ({ userId: approver.userId, approvalStatus: 'Not Reviewed', comments: '', })); - this.collectionSubmitters = []; - if (this.collectionUsers) { - this.collectionUsers.forEach((user: any) => { - if (user.accessLevel >= 2) { - this.collectionSubmitters.push({ ...user }); - } - }); - } - this.setChartSelectionData(); if (this.poam.tenablePluginData) { - try { - const pluginDataObject = JSON.parse(this.poam.tenablePluginData); - this.tenablePluginData = jsonToPlainText(pluginDataObject, {}); - } catch (error) { - this.tenablePluginData = this.poam.tenablePluginData; - } + this.parsePluginData(this.poam.tenablePluginData); } }); } + private parsePluginData(pluginData: string) { + try { + let dataObject: any; + + if (typeof pluginData === 'string') { + dataObject = JSON.parse(pluginData); + } else if (typeof pluginData === 'object') { + dataObject = pluginData; + } else { + throw new Error('Invalid plugin data format'); + } + + this.tenablePluginData = jsonToPlainText(dataObject, {}); + } catch (error) { + this.tenablePluginData = this.poam.tenablePluginData; + } + } + mapTenableSeverity(severity: string) { switch (severity) { case "0": - return 'CAT III - Low'; + return 'CAT III - Informational'; case "1": return 'CAT III - Low'; case "2": return 'CAT II - Medium'; case "3": - return 'CAT I - Critical/High'; + return 'CAT I - High'; case "4": - return 'CAT I - Critical/High'; + return 'CAT I - Critical'; default: return ''; } @@ -494,12 +393,10 @@ ${this.pluginData.description}` || "", async createNewSTIGManagerPoam() { this.validateStigManagerCollection(false); - this.canModifySubmitter = true; forkJoin([ - await this.poamService.getCollection(this.payload.lastCollectionAccessedId, this.payload.userName), await this.collectionService.getUsersForCollection(this.payload.lastCollectionAccessedId), await this.assetService.getAssetsByCollection(this.payload.lastCollectionAccessedId), - ]).subscribe(async ([collection, users, collectionAssets]: any) => { + ]).subscribe(async ([users, collectionAssets]: any) => { this.poam = { poamId: "ADDPOAM", collectionId: this.payload.lastCollectionAccessedId, @@ -518,28 +415,17 @@ ${this.pluginData.description}` || "", this.dates.scheduledCompletionDate = null; this.dates.iavComplyByDate = null; this.dates.submittedDate = new Date(this.poam.submittedDate); - - this.collection = collection; this.collectionUsers = users; this.assets = collectionAssets; this.poamAssets = []; this.poamAssignees = []; this.collectionApprovers = []; - this.collectionApprovers = this.collectionUsers.filter((user: Permission) => user.accessLevel >= 3 || this.user.isAdmin); + this.collectionApprovers = this.collectionUsers.filter((user: Permission) => user.accessLevel >= 3); this.poamApprovers = this.collectionApprovers.map((approver: any) => ({ userId: approver.userId, approvalStatus: 'Not Reviewed', comments: '', })); - this.collectionSubmitters = []; - if (this.collectionUsers) { - this.collectionUsers.forEach((user: any) => { - if (user.accessLevel >= 2) { - this.collectionSubmitters.push({ ...user }); - } - }); - } - this.setChartSelectionData(); (await this.sharedService.getSTIGsFromSTIGMAN()).subscribe({ next: (data) => { this.stigmanSTIGs = data.map((stig: any) => ({ @@ -569,12 +455,10 @@ ${this.pluginData.description}` || "", } async createNewPoam() { - this.canModifySubmitter = true; forkJoin([ - await this.poamService.getCollection(this.payload.lastCollectionAccessedId, this.payload.userName), await this.collectionService.getUsersForCollection(this.payload.lastCollectionAccessedId), await this.assetService.getAssetsByCollection(this.payload.lastCollectionAccessedId), - ]).subscribe(async ([collection, users, collectionAssets]: any) => { + ]).subscribe(async ([users, collectionAssets]: any) => { this.poam = { poamId: "ADDPOAM", collectionId: this.payload.lastCollectionAccessedId, @@ -593,28 +477,17 @@ ${this.pluginData.description}` || "", this.dates.scheduledCompletionDate = null; this.dates.iavComplyByDate = null; this.dates.submittedDate = new Date(this.poam.submittedDate); - - this.collection = collection; this.collectionUsers = users; this.assets = collectionAssets; this.poamAssets = []; this.poamAssignees = []; this.collectionApprovers = []; - this.collectionApprovers = this.collectionUsers.filter((user: Permission) => user.accessLevel >= 3 || this.user.isAdmin); + this.collectionApprovers = this.collectionUsers.filter((user: Permission) => user.accessLevel >= 3); this.poamApprovers = this.collectionApprovers.map((approver: any) => ({ userId: approver.userId, approvalStatus: 'Not Reviewed', comments: '', })); - this.collectionSubmitters = []; - if (this.collectionUsers) { - this.collectionUsers.forEach((user: any) => { - if (user.accessLevel >= 2) { - this.collectionSubmitters.push({ ...user }); - } - }); - } - this.setChartSelectionData(); (await this.sharedService.getSTIGsFromSTIGMAN()).subscribe({ next: (data) => { this.stigmanSTIGs = data.map((stig: any) => ({ @@ -726,15 +599,6 @@ ${this.pluginData.description}` || "", }) } - setChartSelectionData() { - this.collectionSubmitters = []; - if (this.collectionUsers) { - this.collectionUsers.forEach((user: any) => { - if (user.accessLevel >= 2) this.collectionSubmitters.push({ ...user }); - }); - } - } - async approvePoam() { await this.router.navigateByUrl("/poam-processing/poam-approve/" + this.poam.poamId); } @@ -783,7 +647,6 @@ ${this.pluginData.description}` || "", this.poam.submittedDate = this.dates.submittedDate ? format(this.dates.submittedDate, "yyyy-MM-dd") : null; this.poam.iavComplyByDate = this.dates.iavComplyByDate ? format(this.dates.iavComplyByDate, "yyyy-MM-dd") : null; this.poam.requiredResources = this.poam.requiredResources ? this.poam.requiredResources : ""; - this.poam.vulnIdRestricted = this.poam.vulnIdRestricted ? this.poam.vulnIdRestricted : ""; this.poam.poamLog = [{ userId: this.user.userId }]; if (this.poam.status === "Closed") { this.poam.closedDate = format(new Date(), "yyyy-MM-dd"); @@ -906,6 +769,7 @@ ${this.pluginData.description}` || "", this.poam.iavComplyByDate = this.dates.iavComplyByDate ? format(this.dates.iavComplyByDate, "yyyy-MM-dd") : null; this.poam.scheduledCompletionDate = this.dates.scheduledCompletionDate ? format(this.dates.scheduledCompletionDate, "yyyy-MM-dd") : null; this.poam.submittedDate = this.dates.submittedDate ? format(this.dates.submittedDate, "yyyy-MM-dd") : null; + this.poam.hqs = 1 ? true : false; this.savePoam(); } @@ -942,6 +806,10 @@ ${this.pluginData.description}` || "", this.messageService.add({ severity: "error", summary: 'Information', detail: "IAV Comply By Date is required if an IAVM Number is provided." }); return false; } + if (this.poam.adjSeverity && this.poam.adjSeverity != this.poam.rawSeverity && !this.poam.mitigations) { + this.messageService.add({ severity: "error", summary: 'Information', detail: "If Adjusted Severity deviates from the Raw Severity, Mitigations becomes a required field." }); + return false; + } return true; } @@ -1338,6 +1206,7 @@ ${this.pluginData.description}` || "", .map(aaPackage => aaPackage.aaPackage); } + confirm(options: { header: string, message: string, accept: () => void }) { this.confirmationService.confirm({ message: options.message, diff --git a/client/src/app/pages/poam-processing/poam-extend/poam-extend.component.html b/client/src/app/pages/poam-processing/poam-extend/poam-extend.component.html index 013e1455..deeffbc7 100644 --- a/client/src/app/pages/poam-processing/poam-extend/poam-extend.component.html +++ b/client/src/app/pages/poam-processing/poam-extend/poam-extend.component.html @@ -13,8 +13,8 @@
- - + +
@@ -33,106 +33,187 @@ [suggestions]="filteredJustifications" (completeMethod)="filterJustifications($event)" [placeholder]="extensionJustificationPlaceholder" - [style]="{'width':'100%'}"> + [style]="{'width':'100%'}" + [disabled]="accessLevel < 2">
- - - - - Milestone Comments - Milestone Date - Milestone Change Comments - Milestone Change Date - Milestone Status - Milestone Team - - - - - - - - - - - - - {{milestone.milestoneComments}} - - - - - - - - - - {{milestone.milestoneDate | date:'yyyy-MM-dd'}} - - - - - - - - - - {{milestone.milestoneChangeComments}} - - - - - - - - - - {{milestone.milestoneChangeDate | date:'yyyy-MM-dd'}} - - - - - - - - - - {{milestone.milestoneStatus}} - - - - - - - - - - {{milestone.milestoneTeam}} - - - - - - - - - - - - - - No Data to Display - - - + + + + +
+
+ + + + Milestone Comments + Milestone Date + Milestone Change Comments + Milestone Change Date + Milestone Status + Milestone Team + + + + + + + + + + + + + {{milestone.milestoneComments}} + + + + + + + + + + {{milestone.milestoneDate | date:'yyyy-MM-dd'}} + + + + + + + + + + {{milestone.milestoneChangeComments}} + + + + + + + + + + {{milestone.milestoneChangeDate | date:'yyyy-MM-dd'}} + + + + + + + + + + {{milestone.milestoneStatus}} + + + + + + + + + + {{milestone.milestoneTeam}} + + + + + + + + + + + + + + No Data to Display + + + +
+
+ +
+
+
+
-
+ + + +
+
+ +
+
+ + +
+
+
+
+ + + + +
+
+ +
+
+ + +
+
+
+
+ + + + +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+
+
+
+ +
- + diff --git a/client/src/app/pages/poam-processing/poam-extend/poam-extend.component.scss b/client/src/app/pages/poam-processing/poam-extend/poam-extend.component.scss index d6d62e11..8c0d34e6 100644 --- a/client/src/app/pages/poam-processing/poam-extend/poam-extend.component.scss +++ b/client/src/app/pages/poam-processing/poam-extend/poam-extend.component.scss @@ -21,10 +21,54 @@ margin-bottom: 0.5rem; } -p-table { - margin-top: 1rem; -} - .p-button-outlined { margin-right: 1rem; } + +.form-group { + margin-bottom: 15px; +} + +.form-group label { + display: block; + margin-bottom: 5px; +} + +:host ::ng-deep { + .p-card-content { + padding: 0; + } + + .p-stepper-panel { + height: 100%; + } + + .stepper-content-wrapper { + display: flex; + flex-direction: column; + height: 420px; + } + + .stepper-content { + flex: 1; + overflow-y: auto; + padding: 1rem; + margin-bottom: .5rem; + } + + .stepper-buttons { + display: flex; + justify-content: space-between; + padding: 0 1rem 1rem; + border-bottom: 1px solid var(--surface-d); + &.first-panel { + justify-content: flex-end; + } + } + + .p-datatable { + .p-datatable-wrapper { + overflow-x: auto; + } + } +} diff --git a/client/src/app/pages/poam-processing/poam-extend/poam-extend.component.ts b/client/src/app/pages/poam-processing/poam-extend/poam-extend.component.ts index 11176c2a..8c6ba49a 100644 --- a/client/src/app/pages/poam-processing/poam-extend/poam-extend.component.ts +++ b/client/src/app/pages/poam-processing/poam-extend/poam-extend.component.ts @@ -8,16 +8,23 @@ !######################################################################## */ -import { Component, OnInit, OnDestroy, ViewChild, ChangeDetectorRef } from '@angular/core'; +import { Component, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core'; import { Router, ActivatedRoute } from '@angular/router'; import { ConfirmationService } from 'primeng/api'; import { Subscription, forkJoin } from 'rxjs'; -import { addDays, format, isAfter, parseISO } from 'date-fns'; +import { addDays, format, isAfter } from 'date-fns'; import { PoamService } from '../poams.service'; import { UsersService } from '../../admin-processing/user-processing/users.service'; import { SharedService } from '../../../common/services/shared.service'; import { PoamExtensionService } from '../poam-extend/poam-extend.service'; import { MessageService } from 'primeng/api'; +import { AuthGuard } from '../../../core/auth/guards/auth.guard'; + +interface Permission { + userId: number; + collectionId: number; + accessLevel: number; +} @Component({ selector: 'cpat-poam-extend', @@ -26,6 +33,7 @@ import { MessageService } from 'primeng/api'; providers: [ConfirmationService] }) export class PoamExtendComponent implements OnInit, OnDestroy { + protected accessLevel: number; displayExtensionDialog: boolean = false; dates: any = {}; poam: any; @@ -51,15 +59,49 @@ export class PoamExtendComponent implements OnInit, OnDestroy { ]; filteredJustifications: string[] = []; extensionTimeOptions = [ - { label: '0 Days', value: 0 }, + { label: '3 Days', value: 3 }, + { label: '7 Days', value: 7 }, + { label: '14 Days', value: 14 }, { label: '30 Days', value: 30 }, { label: '60 Days', value: 60 }, { label: '90 Days', value: 90 }, { label: '180 Days', value: 180 }, { label: '365 Days', value: 365 } ]; + residualRiskOptions = [ + { label: 'Very Low', value: 'Very Low' }, + { label: 'Low', value: 'Low' }, + { label: 'Moderate', value: 'Moderate' }, + { label: 'High', value: 'High' }, + { label: 'Very High', value: 'Very High' }, + ]; + + likelihoodOptions = [ + { label: 'Very Low', value: 'Very Low' }, + { label: 'Low', value: 'Low' }, + { label: 'Moderate', value: 'Moderate' }, + { label: 'High', value: 'High' }, + { label: 'Very High', value: 'Very High' }, + ]; + + relevanceOfThreatOptions = [ + { label: 'Very Low', value: 'Very Low' }, + { label: 'Low', value: 'Low' }, + { label: 'Moderate', value: 'Moderate' }, + { label: 'High', value: 'High' }, + { label: 'Very High', value: 'Very High' }, + ]; + + impactRatingOptions = [ + { label: 'Very Low', value: 'Very Low' }, + { label: 'Low', value: 'Low' }, + { label: 'Moderate', value: 'Moderate' }, + { label: 'High', value: 'High' }, + { label: 'Very High', value: 'Very High' }, + ]; selectedCollection: any; user: any; + payload: any; completionDate: any; completionDateWithExtension: any; labelList: any; @@ -93,19 +135,38 @@ export class PoamExtendComponent implements OnInit, OnDestroy { async setPayload() { this.user = null; + this.payload = null; + this.accessLevel = 0; (await this.userService.getCurrentUser()).subscribe({ next: (response: any) => { if (response?.userId) { this.user = response; + const mappedPermissions = this.user.permissions?.map((permission: Permission) => ({ + collectionId: permission.collectionId, + accessLevel: permission.accessLevel, + })); + + this.payload = { + ...this.user, + collections: mappedPermissions + }; + if (mappedPermissions.length > 0) { + const selectedPermissions = this.payload.collections.find( + (x: { collectionId: any; }) => x.collectionId == this.payload.lastCollectionAccessedId + ); + + if (selectedPermissions) { + this.accessLevel = selectedPermissions.accessLevel; + } + } this.getData(); - } else { - console.error('User data is not available or user is not active'); } }, error: (error) => { console.error('An error occurred:', error); } }); + this.cdr.detectChanges(); } async getData() { @@ -127,6 +188,13 @@ export class PoamExtendComponent implements OnInit, OnDestroy { this.poam = { poamId: +poamData.poamId, status: poamData.status, + mitigations: poamData.mitigations, + requiredResources: poamData.requiredResources, + residualRisk: poamData.residualRisk, + likelihood: poamData.likelihood, + relevanceOfThreat: poamData.relevanceOfThreat, + impactRating: poamData.impactRating, + impactDescription: poamData.impactDescription, extensionTimeAllowed: extensionData.extensionTimeAllowed, extensionJustification: extensionData.extensionJustification, scheduledCompletionDate: new Date(extensionData.scheduledCompletionDate), @@ -142,6 +210,13 @@ export class PoamExtendComponent implements OnInit, OnDestroy { extensionTimeAllowed: 0, extensionJustification: '', scheduledCompletionDate: '', + mitigations: '', + requiredResources: '', + residualRisk: '', + likelihood: '', + relevanceOfThreat: '', + impactRating: '', + impactDescription: '', }; this.extensionJustification = ''; this.completionDateWithExtension = this.poam.scheduledCompletionDate.substr(0, 10).replaceAll('-', '/'); @@ -333,6 +408,13 @@ export class PoamExtendComponent implements OnInit, OnDestroy { extensionTimeAllowed: this.poam.extensionTimeAllowed, extensionJustification: this.extensionJustification, status: 'Extension Requested', + mitigations: this.poam.mitigations, + requiredResources: this.poam.requiredResources, + residualRisk: this.poam.residualRisk, + likelihood: this.poam.likelihood, + relevanceOfThreat: this.poam.relevanceOfThreat, + impactRating: this.poam.impactRating, + impactDescription: this.poam.impactDescription, poamLog: [{ userId: this.user.userId }], }; @@ -341,7 +423,19 @@ export class PoamExtendComponent implements OnInit, OnDestroy { } try { - await this.poamExtensionService.putPoamExtension(extensionData); + + (await this.poamExtensionService.putPoamExtension(extensionData)).subscribe({ + next: (res) => { + if (res.null || res.null == "null") { + this.messageService.add({ severity: "error", summary: 'Information', detail: "Unexpected error adding POAM Extension" }); + } else { + this.messageService.add({ severity: "success", summary: 'Success', detail: `Added POAM: ${res.poamId}` }); + } + }, + error: () => { + this.messageService.add({ severity: "error", summary: 'Information', detail: "Unexpected error adding POAM Extension" }); + } + }); if (this.poam.extensionTimeAllowed > 0) { await this.poamService.updatePoamStatus(this.poamId, extensionData); } diff --git a/client/src/app/pages/poam-processing/poam-manage/poam-manage.component.html b/client/src/app/pages/poam-processing/poam-manage/poam-manage.component.html index 9fd2ee61..14c327b3 100644 --- a/client/src/app/pages/poam-processing/poam-manage/poam-manage.component.html +++ b/client/src/app/pages/poam-processing/poam-manage/poam-manage.component.html @@ -31,25 +31,25 @@ @@ -59,6 +59,6 @@
diff --git a/client/src/app/pages/poam-processing/poam-manage/poam-manage.component.ts b/client/src/app/pages/poam-processing/poam-manage/poam-manage.component.ts index d024bb7e..2a1354e3 100644 --- a/client/src/app/pages/poam-processing/poam-manage/poam-manage.component.ts +++ b/client/src/app/pages/poam-processing/poam-manage/poam-manage.component.ts @@ -37,7 +37,7 @@ interface LabelInfo { export class PoamManageComponent implements OnInit, AfterViewInit, OnDestroy { advancedSeverityseverityPieChartData: any[] = []; advancedStatusPieChartData: any[] = []; - approvalColumns = ['POAM ID', 'Adjusted Severity', 'Approval Status', 'Manage']; + approvalColumns = ['POAM ID', 'Adjusted Severity', 'Approval Status', 'POAM']; private subs = new SubSink(); public isLoggedIn = false; public monthlyPoamStatus: any[] = []; @@ -186,7 +186,7 @@ export class PoamManageComponent implements OnInit, AfterViewInit, OnDestroy { } updateAdvancedPieChart(): void { - const severityOrder = ['CAT I - Critical/High', 'CAT II - Medium', 'CAT III - Low']; + const severityOrder = ['CAT I - Critical' || 'CAT I - High', 'CAT II - Medium', 'CAT III - Low' || 'CAT III - Informational']; const severityLabel = ['CAT I', 'CAT II', 'CAT III']; const statusOrder = ['Submitted', 'Approved', 'Closed', 'Rejected', 'Extension Requested', 'Expired', 'Draft']; diff --git a/client/src/app/pages/poam-processing/poams.service.ts b/client/src/app/pages/poam-processing/poams.service.ts index 09f661b0..b5cd8d7d 100644 --- a/client/src/app/pages/poam-processing/poams.service.ts +++ b/client/src/app/pages/poam-processing/poams.service.ts @@ -38,12 +38,6 @@ export class PoamService { return new HttpHeaders().set('Authorization', 'Bearer ' + token); } - async getCollection(id: string, userName: string) { - const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/collection/${id}/user/${userName}`, { headers }) - .pipe(catchError(this.handleError)); - } - async getCollectionPoamStatus(id: string) { const headers = await this.getAuthHeaders(); return this.http.get(`${this.cpatApiBase}/metrics/collection/${id}/poamstatus`, { headers }) @@ -74,27 +68,27 @@ export class PoamService { .pipe(catchError(this.handleError)); } - async getPoam(id: string, includeApprovers: boolean = false, includeAssignees: boolean = false, includeAssets: boolean = false) { + async getPoam(poamId: string, includeApprovers: boolean = false, includeAssignees: boolean = false, includeAssets: boolean = false) { const params = new HttpParams() .set('approvers', includeApprovers.toString()) .set('assignees', includeAssignees.toString()) .set('assets', includeAssets.toString()); const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/poam/${id}`, { + return this.http.get(`${this.cpatApiBase}/poam/${poamId}`, { headers: headers, params: params }).pipe(catchError(this.handleError)); } - async getPoamsByCollection(id: string, includeApprovers: boolean = false, includeAssignees: boolean = false, includeAssets: boolean = false) { + async getPoamsByCollection(collectionId: string, includeApprovers: boolean = false, includeAssignees: boolean = false, includeAssets: boolean = false) { const params = new HttpParams() .set('approvers', includeApprovers.toString()) .set('assignees', includeAssignees.toString()) .set('assets', includeAssets.toString()); const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/poams/collection/${id}`, { + return this.http.get(`${this.cpatApiBase}/poams/collection/${collectionId}`, { headers: headers, params: params }).pipe(catchError(this.handleError)); @@ -119,15 +113,15 @@ export class PoamService { .pipe(catchError(this.handleError)); } - async getPoamAssets(id: string) { + async getPoamAssets(poamId: string) { const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/poamAssets/poam/${id}`, { headers }) + return this.http.get(`${this.cpatApiBase}/poamAssets/poam/${poamId}`, { headers }) .pipe(catchError(this.handleError)); } - async getPoamAssignees(id: string) { + async getPoamAssignees(poamId: string) { const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/poamAssignees/poam/${id}`, { headers }) + return this.http.get(`${this.cpatApiBase}/poamAssignees/poam/${poamId}`, { headers }) .pipe(catchError(this.handleError)); } @@ -195,21 +189,9 @@ export class PoamService { .pipe(catchError(this.handleError)); } - async getPoamApprovers(id: string) { - const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/poamApprovers/${id}`, { headers }) - .pipe(catchError(this.handleError)); - } - - async getPoamApproversByCollectionUser(collectionId: string, userId: string) { - const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/poamApprovers/collection/${collectionId}/${userId}`, { headers }) - .pipe(catchError(this.handleError)); - } - - async getPoamApproversByUserId(userId: string) { + async getPoamApprovers(poamId: string) { const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/poamApprovers/user/${userId}`, { headers }) + return this.http.get(`${this.cpatApiBase}/poamApprovers/${poamId}`, { headers }) .pipe(catchError(this.handleError)); } @@ -281,17 +263,17 @@ export class PoamService { .pipe(catchError(this.handleError)); } - async getPoamLabels(id: any) { + async getPoamLabels(collectionId: any) { const headers = await this.getAuthHeaders(); return this.http - .get(`${this.cpatApiBase}/poamLabels/${id}`, { headers }) + .get(`${this.cpatApiBase}/poamLabels/${collectionId}`, { headers }) .pipe(catchError(this.handleError)); } - async getPoamLabelsByPoam(id: any) { + async getPoamLabelsByPoam(poamId: any) { const headers = await this.getAuthHeaders(); return this.http - .get(`${this.cpatApiBase}/poamLabels/poam/${id}`, { headers }) + .get(`${this.cpatApiBase}/poamLabels/poam/${poamId}`, { headers }) .pipe(catchError(this.handleError)); } @@ -312,53 +294,53 @@ export class PoamService { return this.http.delete(`${this.cpatApiBase}/poamLabel/poam/${poamId}/label/${labelId}`, options); } - async getAvailablePoamStatus(userId: string) { + async getAvailablePoamStatus() { const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/metrics/available/${userId}/poamstatus`, { headers }) + return this.http.get(`${this.cpatApiBase}/metrics/available/poamstatus`, { headers }) .pipe(catchError(this.handleError)); } - async getAvailablePoamLabel(userId: string) { + async getAvailablePoamLabel() { const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/metrics/available/${userId}/poamlabel`, { headers }) + return this.http.get(`${this.cpatApiBase}/metrics/available/poamlabel`, { headers }) .pipe(catchError(this.handleError)); } - async getAvailablePoamSeverity(userId: string) { + async getAvailablePoamSeverity() { const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/metrics/available/${userId}/poamseverity`, { headers }) + return this.http.get(`${this.cpatApiBase}/metrics/available/poamseverity`, { headers }) .pipe(catchError(this.handleError)); } - async getAvailableMonthlyPoamStatus(userId: string) { + async getAvailableMonthlyPoamStatus() { const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/metrics/available/${userId}/monthlypoamstatus`, { headers }) + return this.http.get(`${this.cpatApiBase}/metrics/available/monthlypoamstatus`, { headers }) .pipe(catchError(this.handleError)); } - async getAvailablePoamScheduledCompletion(userId: string) { + async getAvailablePoamScheduledCompletion() { const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/metrics/available/${userId}/poamScheduledCompletion`, { headers }) + return this.http.get(`${this.cpatApiBase}/metrics/available/poamScheduledCompletion`, { headers }) .pipe(catchError(this.handleError)); } async getAvailableCollectionPoamCounts(userId: any) { const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/metrics/available/${userId}/collectionpoamcount`, { headers }) + return this.http.get(`${this.cpatApiBase}/metrics/available/collectionpoamcount`, { headers }) .pipe(catchError(this.handleError)); } - async getAvailablePoams(userId: string) { + async getAvailablePoams() { const headers = await this.getAuthHeaders(); - return this.http.get(`${this.cpatApiBase}/poams/${userId}`, { headers }) + return this.http.get(`${this.cpatApiBase}/poams`, { headers }) .pipe(catchError(this.handleError)); } async getAvailablePoamLabels(userId: any) { const headers = await this.getAuthHeaders(); return this.http - .get(`${this.cpatApiBase}/poamLabels/available/${userId}`, { headers }) + .get(`${this.cpatApiBase}/poamLabels`, { headers }) .pipe(catchError(this.handleError)); } } diff --git a/client/src/index.html b/client/src/index.html index 5b19f126..aae2f182 100644 --- a/client/src/index.html +++ b/client/src/index.html @@ -39,7 +39,7 @@ extraScopes: "", scopePrefix: "", claims: { - scope: "c-pat:read c-pat:op openid profile offline_access", + scope: "scope", username: "preferred_username", servicename: "clientId", fullname: "name",