Skip to content

Commit

Permalink
squashed merge of main
Browse files Browse the repository at this point in the history
  • Loading branch information
csmig committed Jan 30, 2024
1 parent fe4b719 commit f1a6eeb
Show file tree
Hide file tree
Showing 39 changed files with 13,730 additions and 5,339 deletions.
7 changes: 7 additions & 0 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
# Required
version: 2

# Set the OS, Python version and other tools you might need
build:
os: ubuntu-22.04
tools:
python: "3.12"


# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/conf.py
Expand Down
47 changes: 47 additions & 0 deletions api/source/controllers/Metrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,35 @@ async function getCollectionMetrics (req, res, next, {style, aggregation, firstR
}
}

async function getMetaMetrics (req, res, next, {style, aggregation, firstRowOnly = false}) {
try {
const returnType = req.query.format || 'json'
const inPredicates = {
collectionIds: req.query.collectionId,
benchmarkIds: req.query.benchmarkId,
revisionIds: req.query.revisionId
}
const rows = await MetricsService.queryMetaMetrics({
inPredicates,
userId: req.userObject.userId,
style,
aggregation,
returnType
})
if (returnType === 'csv') {
res.type('text/csv')
res.send(csvStringify(rows, {header: true}))
}
else {
res.json(firstRowOnly ? rows[0] : rows)
}
}
catch (e) {
next(e)
}
}


module.exports.getMetricsDetailByCollection = async function (req, res, next) {
await getCollectionMetrics(req, res, next, {style: 'detail', aggregation: 'unagg'})
}
Expand Down Expand Up @@ -67,3 +96,21 @@ module.exports.getMetricsSummaryByCollectionAggLabel = async function (req, res,
module.exports.getMetricsSummaryByCollectionAggStig = async function (req, res, next) {
await getCollectionMetrics(req, res, next, {style: 'summary', aggregation: 'stig'})
}
module.exports.getMetricsDetailByMeta = async function (req, res, next) {
await getMetaMetrics(req, res, next, {style: 'detail', aggregation: 'meta', firstRowOnly: true})
}
module.exports.getMetricsDetailByMetaAggCollection = async function (req, res, next) {
await getMetaMetrics(req, res, next, {style: 'detail', aggregation: 'collection'})
}
module.exports.getMetricsDetailByMetaAggStig = async function (req, res, next) {
await getMetaMetrics(req, res, next, {style: 'detail', aggregation: 'metaStig'})
}
module.exports.getMetricsSummaryByMeta = async function (req, res, next) {
await getMetaMetrics(req, res, next, {style: 'summary', aggregation: 'meta', firstRowOnly: true})
}
module.exports.getMetricsSummaryByMetaAggCollection = async function (req, res, next) {
await getMetaMetrics(req, res, next, {style: 'summary', aggregation: 'collection'})
}
module.exports.getMetricsSummaryByMetaAggStig = async function (req, res, next) {
await getMetaMetrics(req, res, next, {style: 'summary', aggregation: 'metaStig'})
}
4 changes: 2 additions & 2 deletions api/source/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion api/source/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "stig-management-api",
"version": "1.4.1",
"version": "1.4.2",
"description": "An API for managing evaluations of Security Technical Implementation Guide (STIG) assessments.",
"main": "index.js",
"scripts": {
Expand Down
156 changes: 155 additions & 1 deletion api/source/service/MetricsService.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ const dbUtils = require('./utils')

module.exports.queryMetrics = async function ({
inPredicates = {},
inProjections = [],
userId,
aggregation = 'unagg',
style = 'detail',
Expand All @@ -15,6 +14,7 @@ module.exports.queryMetrics = async function ({
}

// CTE processing
// This CTE retreives the granted Asset/STIG pairs for a single collection
const cteProps = {
columns: [
'distinct c.collectionId',
Expand Down Expand Up @@ -173,6 +173,132 @@ module.exports.queryMetrics = async function ({
return (rows || [])
}

module.exports.queryMetaMetrics = async function ({
inPredicates = {},
userId,
aggregation = 'meta',
style = 'detail',
returnType = 'json'
}) {
const predicates = {
statements: [],
binds: []
}
// CTE processing
// This CTE retreives the granted Asset/STIG pairs across all collections (or the requested ones)
const cteProps = {
columns: [
'distinct c.collectionId',
'sa.benchmarkId',
'a.assetId',
'sa.saId'
],
joins: [
'collection c',
'left join collection_grant cg on c.collectionId = cg.collectionId',
'inner join asset a on c.collectionId = a.collectionId and a.state = "enabled"',
'left join stig_asset_map sa on a.assetId = sa.assetId',
'left join user_stig_asset_map usa on sa.saId = usa.saId'
],
predicates: {
statements: [
'(cg.userId = ? AND CASE WHEN cg.accessLevel = 1 THEN usa.userId = cg.userId ELSE TRUE END)',
'c.state = "enabled"'
],
binds: [
userId
]
}
}
if (inPredicates.benchmarkIds) {
cteProps.predicates.statements.push(
'sa.benchmarkId IN ?'
)
cteProps.predicates.binds.push([inPredicates.benchmarkIds])
}
if (inPredicates.collectionIds) {
cteProps.predicates.statements.push(
'c.collectionId IN ?'
)
cteProps.predicates.binds.push([inPredicates.collectionIds])
}
if (inPredicates.revisionIds) {
cteProps.joins.push(
'left join default_rev dr on c.collectionId = dr.collectionId and sa.benchmarkId = dr.benchmarkId',
'left join revision rev on dr.revId = rev.revId'
)
cteProps.predicates.statements.push(
'rev.revId IN ?'
)
cteProps.predicates.binds.push([inPredicates.revisionIds])
}
const cteQuery = dbUtils.makeQueryString({
columns: cteProps.columns,
joins: cteProps.joins,
predicates: cteProps.predicates
})
const ctes = [
`granted as (${cteQuery})`
]
// Main query
const columns = returnType === 'csv' ? [...baseColsFlat[aggregation]] : [...baseCols[aggregation]]
const joins = [
'granted',
'left join asset a on granted.assetId = a.assetId',
'left join stig_asset_map sa on granted.saId = sa.saId',
'left join default_rev dr on granted.collectionId = dr.collectionId and sa.benchmarkId = dr.benchmarkId',
'left join revision rev on dr.revId = rev.revId',
'left join stig on rev.benchmarkId = stig.benchmarkId'
]
const groupBy = []
const orderBy = []
switch (aggregation) {
case 'meta':
predicates.statements.push('sa.benchmarkId IS NOT NULL')
break
case 'collection':
joins.push('left join collection c on granted.collectionId = c.collectionId')
groupBy.push('c.collectionId')
orderBy.push('c.name')
break
case 'metaStig':
predicates.statements.push('sa.benchmarkId IS NOT NULL')
groupBy.push('rev.revId')
orderBy.push('rev.benchmarkId')
break
}
if (style === 'detail') {
if (returnType === 'csv') {
columns.push(...colsMetricsDetailAgg)
}
else {
columns.push(sqlMetricsDetailAgg)
}
}
else { //style: 'summary'
if (returnType === 'csv') {
columns.push(...colsMetricsSummaryAgg)
}
else {
columns.push(sqlMetricsSummaryAgg)
}
}
const query = dbUtils.makeQueryString({
ctes,
columns,
joins,
predicates,
groupBy,
orderBy
})

let [rows, fields] = await dbUtils.pool.query(
query,
[...cteProps.predicates.binds, ...predicates.binds]
)
return (rows || [])
}

const sqlMetricsDetail = `json_object(
'assessments', rev.ruleCount,
'assessmentsBySeverity', json_object(
Expand Down Expand Up @@ -463,6 +589,20 @@ const baseCols = {
'cl.color',
'cl.description',
'count(distinct a.assetId) as assets'
],
meta: [
'count(distinct granted.collectionId) as collections',
'count(distinct a.assetId) as assets',
'count(distinct sa.benchmarkId) as stigs',
'count(sa.saId) as checklists'
],
metaStig: [
'rev.benchmarkId',
'stig.title',
'rev.revisionStr',
'count(distinct granted.collectionId) as collections',
'count(distinct a.assetId) as assets',
'rev.ruleCount'
]
}
const baseColsFlat = {
Expand Down Expand Up @@ -499,5 +639,19 @@ const baseColsFlat = {
'BIN_TO_UUID(cl.uuid,1) as labelId',
'cl.name',
'count(distinct a.assetId) as assets'
],
meta: [
'count(distinct granted.collectionId) as collections',
'count(distinct a.assetId) as assets',
'count(distinct sa.benchmarkId) as stigs',
'count(sa.saId) as checklists'
],
metaStig: [
'rev.benchmarkId',
'stig.title',
'rev.revisionStr',
'count(distinct granted.collectionId) as collections',
'count(distinct a.assetId) as assets',
'rev.ruleCount'
]
}
Loading

0 comments on commit f1a6eeb

Please sign in to comment.