Skip to content

Commit

Permalink
Merge branch 'main' into dependabot/npm_and_yarn/main/testing-library…
Browse files Browse the repository at this point in the history
…/react-16.0.0
  • Loading branch information
daniele-mng committed Jul 11, 2024
2 parents d99214f + 3471fbf commit e44b8a4
Show file tree
Hide file tree
Showing 16 changed files with 421 additions and 21 deletions.
2 changes: 1 addition & 1 deletion .docker/prod.Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
ARG VERSION=stable
ARG DEBIAN_FRONTEND=noninteractive

FROM --platform=linux/amd64 debian:stable-slim as builder
FROM debian:stable-slim as builder

ENV NODE_VERSION=node_18.x
ENV NODE_KEYRING=/usr/share/keyrings/nodesource.gpg
Expand Down
26 changes: 26 additions & 0 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Build and Push to Greenbone Registry

on:
push:
branches: [ main ]
tags: ["v*"]
pull_request:
branches: [ main ]
workflow_dispatch:
inputs:
ref-name:
type: string
description: "The ref to build a container image from. For example a tag v23.0.0."
required: true

jobs:
build:
name: Build and Push to Greenbone Registry
uses: greenbone/workflows/.github/workflows/container-build-push-2nd-gen.yml@main
with:
image-url: community/gsa
image-labels: |
org.opencontainers.image.vendor=Greenbone
org.opencontainers.image.base.name=greenbone/gsad
ref-name: ${{ inputs.ref-name }}
secrets: inherit
4 changes: 3 additions & 1 deletion allowedSnakeCase.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,10 @@ module.exports = [
'general_permissions',
'get_aggregate',
'get_aggregates_response',
'get_capabilities',
'get_config_family_response',
'get_config_nvt_response',
'get_features_response',
'get_feeds',
'get_feeds_response',
'get_identifier',
Expand Down Expand Up @@ -236,13 +238,13 @@ module.exports = [
'groups_data',
'gsad_response',
'has_av_duration',
'_has_caps',
'has_detection',
'has_duration',
'has_reference',
'has_selected',
'has_severity_filter',
'header_title',
'help_response',
'highest_severity',
'high_per_host',
'host_allow',
Expand Down
13 changes: 13 additions & 0 deletions src/gmp/capabilities/__tests__/capabilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,19 @@ describe('Capabilities tests', () => {
}
expect(i).toEqual(4);
});

test('should handle features', () => {
const featureList = [
{name: 'ENABLED_FEATURE_1', _enabled: 1},
{name: 'DISABLED_FEATURE', _enabled: 0},
{name: 'ENABLED_FEATURE_2', _enabled: 1},
];
const caps = new Capabilities(['everything'], featureList);
expect(caps.featureEnabled('ENABLED_FEATURE_1')).toBe(true);
expect(caps.featureEnabled('DISABLED_FEATURE')).toBe(false);
expect(caps.featureEnabled('enabled_feature_2')).toBe(true);
expect(caps.featureEnabled('UNDEFINED_FEATURE')).toBe(false);
});
});

// vim: set ts=2 sw=2 tw=80:
27 changes: 21 additions & 6 deletions src/gmp/capabilities/capabilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/


import {isDefined} from 'gmp/utils/identity';
import {map} from 'gmp/utils/array';
import {forEach, map} from 'gmp/utils/array';
import {pluralizeType} from 'gmp/utils/entitytype';
import {parseBoolean} from 'gmp/parser';

const types = {
audit: 'task',
Expand Down Expand Up @@ -51,24 +51,35 @@ const convertType = type => {
};

class Capabilities {
constructor(cap_names) {
this._has_caps = isDefined(cap_names);
constructor(cap_names, featuresList) {
this._hasCaps = isDefined(cap_names);
this._hasFeatures = isDefined(featuresList);

let caps;
let featuresEnabled = {};

if (this._has_caps) {
if (this._hasCaps) {
caps = map(cap_names, name => name.toLowerCase());
}

if (this._hasFeatures) {
forEach(featuresList, feature => {
featuresEnabled[feature.name.toUpperCase()] = parseBoolean(
feature._enabled,
);
});
}

this._capabilities = new Set(caps);
this._featuresEnabled = featuresEnabled;
}

[Symbol.iterator]() {
return this._capabilities[Symbol.iterator]();
}

areDefined() {
return this._has_caps;
return this._hasCaps;
}

has(name) {
Expand Down Expand Up @@ -102,6 +113,10 @@ class Capabilities {
get length() {
return this._capabilities.size;
}

featureEnabled(feature) {
return this._featuresEnabled[feature.toUpperCase()] == true;
}
}

export default Capabilities;
Expand Down
55 changes: 55 additions & 0 deletions src/gmp/commands/__tests__/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,58 @@ describe('UserCommand transformSettingName() function tests', () => {
expect(transformSettingName(str4)).toEqual('foobar');
});
});

describe('UserCommand capabilities tests', () => {
test('should get capabilities', () => {
const response = createResponse({
get_capabilities: {
help_response: {
schema: {
command: [
{
name: 'get_reports',
},
{
name: 'get_tasks',
},
],
},
},
get_features_response: {
feature: [
{
_enabled: 1,
name: 'TEST_FEATURE_1',
},
{
_enabled: 1,
name: 'TEST_FEATURE_2',
},
],
},
},
});
const fakeHttp = createHttp(response);
const cmd = new UserCommand(fakeHttp);

cmd.currentCapabilities().then(resp => {
const {data: caps} = resp;

expect(fakeHttp.request).toHaveBeenCalledWith('get', {
args: {
cmd: 'get_capabilities',
},
});

expect(caps._hasCaps).toBe(true);
expect(caps.mayAccess('report')).toBe(true);
expect(caps.mayAccess('task')).toBe(true);
expect(caps.mayAccess('user')).toBe(false);

expect(caps._hasFeatures).toBe(true);
expect(caps.featureEnabled('test_feature_1')).toBe(true);
expect(caps.featureEnabled('TEST_FEATURE_2')).toBe(true);
expect(caps.featureEnabled('TEST_FEATURE_3')).toBe(false);
});
});
});
3 changes: 2 additions & 1 deletion src/gmp/commands/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,9 @@ export class UserCommand extends EntityCommand {
).then(response => {
const {data} = response;
const {command: commands} = data.get_capabilities.help_response.schema;
const featuresList = data.get_capabilities.get_features_response.feature;
const caps = map(commands, command => command.name);
return response.setData(new Capabilities(caps));
return response.setData(new Capabilities(caps, featuresList));
});
}

Expand Down
2 changes: 1 addition & 1 deletion src/gmp/models/cve.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/


import {isArray, isDefined} from 'gmp/utils/identity';
import {isEmpty} from 'gmp/utils/string';
import {map} from 'gmp/utils/array';
Expand All @@ -27,6 +26,7 @@ class Cve extends Info {

ret.name = element.name;
ret.id = element.name;
ret.epss = element.epss;

return ret;
}
Expand Down
30 changes: 30 additions & 0 deletions src/web/components/conditionalRoute/ConditionalRoute.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/* SPDX-FileCopyrightText: 2024 Greenbone AG
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import PropTypes from 'prop-types';

import {Route, Redirect} from 'react-router-dom';
import useCapabilities from 'web/hooks/useCapabilities';

const ConditionalRoute = ({component: Component, feature, ...rest}) => {
const capabilities = useCapabilities();
const isEnabled = capabilities.featureEnabled(feature);

return (
<Route
render={props =>
isEnabled ? <Component {...props} /> : <Redirect to="/notfound" />
}
{...rest}
/>
);
};

ConditionalRoute.propTypes = {
component: PropTypes.elementType.isRequired,
feature: PropTypes.string.isRequired,
};

export default ConditionalRoute;
47 changes: 47 additions & 0 deletions src/web/components/conditionalRoute/__tests__/ConditionalRoute.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* SPDX-FileCopyrightText: 2024 Greenbone AG
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import {describe, test, expect} from '@gsa/testing';
import {screen, rendererWith} from 'web/utils/testing';
import {Route, MemoryRouter} from 'react-router-dom';
import ConditionalRoute from 'web/components/conditionalRoute/ConditionalRoute';
import Capabilities from 'gmp/capabilities/capabilities';

const MockComponent = () => <div>Mock Component</div>;

describe('ConditionalRoute', () => {
const featureList = [
{name: 'ENABLED_FEATURE', _enabled: 1},
{name: 'DISABLED_FEATURE', _enabled: 0},
];
const capabilities = new Capabilities(['everything'], featureList);
const {render} = rendererWith({capabilities, router: true});
test.each([
{
feature: 'ENABLED_FEATURE',
expectedText: 'Mock Component',
description: 'renders the component when the feature is enabled',
},
{
feature: 'DISABLED_FEATURE',
expectedText: 'Not Found',
description: 'redirects when the feature is disabled',
},
{
feature: 'unknown-feature',
expectedText: 'Not Found',
description: 'does not render the component for an unknown feature',
},
])('$description', ({feature, expectedText}) => {
render(
<MemoryRouter>
<ConditionalRoute component={MockComponent} feature={feature} />
<Route path="/notfound" render={() => <div>Not Found</div>} />
</MemoryRouter>,
);

expect(screen.getByText(expectedText)).toBeVisible();
});
});
Loading

0 comments on commit e44b8a4

Please sign in to comment.