Skip to content

Commit b5a068f

Browse files
committed
feat(argocd): add permission support for argocd
1 parent 9bb8c47 commit b5a068f

File tree

17 files changed

+192
-3
lines changed

17 files changed

+192
-3
lines changed

plugins/argocd-common/.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);

plugins/argocd-common/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# argocd-common
2+
3+
Welcome to the argocd-common plugin!
4+
5+
This plugin contains common utilities for the argocd plugin.
6+
7+
# Argocd plugin for Backstage
8+
9+
The Argocd plugin displays the information about your argocd applications in your Backstage application.
10+
11+
For more information about Argocd plugin, see the [Argocd plugin documentation](https://github.com/janus-idp/backstage-plugins/tree/main/plugins/argocd) on GitHub.

plugins/argocd-common/package.json

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"name": "@janus-idp/backstage-plugin-argocd-common",
3+
"version": "0.1.0",
4+
"main": "src/index.ts",
5+
"types": "src/index.ts",
6+
"license": "Apache-2.0",
7+
"publishConfig": {
8+
"access": "public",
9+
"main": "dist/index.cjs.js",
10+
"module": "dist/index.esm.js",
11+
"types": "dist/index.d.ts"
12+
},
13+
"backstage": {
14+
"role": "common-library",
15+
"supported-versions": "1.26.5"
16+
},
17+
"sideEffects": false,
18+
"scripts": {
19+
"build": "backstage-cli package build",
20+
"clean": "backstage-cli package clean",
21+
"lint": "backstage-cli package lint",
22+
"postpack": "backstage-cli packag e postpack",
23+
"prepack": "backstage-cli package prepack",
24+
"test": "backstage-cli package test --passWithNoTests --coverage",
25+
"tsc": "tsc"
26+
},
27+
"dependencies": {
28+
"@backstage/plugin-permission-common": "^0.7.13"
29+
},
30+
"devDependencies": {
31+
"@backstage/cli": "0.26.6"
32+
},
33+
"files": [
34+
"dist"
35+
],
36+
"repository": {
37+
"type": "git",
38+
"url": "https://github.com/janus-idp/backstage-plugins",
39+
"directory": "plugins/argocd-common"
40+
},
41+
"keywords": [
42+
"support:production",
43+
"lifecycle:active",
44+
"backstage",
45+
"plugin"
46+
],
47+
"homepage": "https://red.ht/rhdh",
48+
"bugs": "https://github.com/janus-idp/backstage-plugins/issues"
49+
}

plugins/argocd-common/src/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/**
2+
* Common functionalities for the argocd plugin.
3+
*
4+
* @packageDocumentation
5+
*/
6+
7+
export * from './permissions';
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { createPermission } from '@backstage/plugin-permission-common';
2+
3+
export const argocdViewPermission = createPermission({
4+
name: 'argocd.view.read',
5+
attributes: {
6+
action: 'read',
7+
},
8+
});
9+
10+
/**
11+
* List of all permissions on permission polices.
12+
*/
13+
export const argocdPermissions = [argocdViewPermission];

plugins/argocd-common/tsconfig.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": "@backstage/cli/config/tsconfig.json",
3+
"include": ["src", "dev"],
4+
"exclude": ["node_modules"],
5+
"compilerOptions": {
6+
"outDir": "../../dist-types/plugins/argocd-common",
7+
"rootDir": "."
8+
}
9+
}

plugins/argocd-common/turbo.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": ["//"],
3+
"pipeline": {
4+
"tsc": {
5+
"outputs": ["../../dist-types/plugins/argocd-common/**"],
6+
"dependsOn": ["^tsc"]
7+
}
8+
}
9+
}

plugins/argocd/dev/index.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import { ConfigReader } from '@backstage/config';
55
import { configApiRef } from '@backstage/core-plugin-api';
66
import { createDevApp } from '@backstage/dev-utils';
77
import { EntityProvider } from '@backstage/plugin-catalog-react';
8-
import { TestApiProvider } from '@backstage/test-utils';
8+
import { permissionApiRef } from '@backstage/plugin-permission-react';
9+
import { MockPermissionApi, TestApiProvider } from '@backstage/test-utils';
910

1011
import { Box } from '@material-ui/core';
1112
import { createDevAppThemes } from '@redhat-developer/red-hat-developer-hub-theme';
@@ -47,7 +48,7 @@ const mockEntity: Entity = {
4748
owner: 'user:guest',
4849
},
4950
};
50-
51+
const mockPermissionApi = new MockPermissionApi();
5152
export class MockArgoCDApiClient implements ArgoCDApi {
5253
async listApps(_options: listAppsOptions): Promise<any> {
5354
return { items: [mockApplication, preProdApplication, prodApplication] };
@@ -72,6 +73,7 @@ createDevApp()
7273
apis={[
7374
[configApiRef, new ConfigReader(mockArgocdConfig)],
7475
[argoCDApiRef, new MockArgoCDApiClient()],
76+
[permissionApiRef, mockPermissionApi],
7577
]}
7678
>
7779
<EntityProvider entity={mockEntity}>

plugins/argocd/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
"@backstage/core-plugin-api": "^1.9.2",
3434
"@backstage/plugin-catalog-react": "^1.12.0",
3535
"@backstage/theme": "^0.5.5",
36+
"@janus-idp/backstage-plugin-argocd-common": "0.1.0",
37+
"@backstage/plugin-permission-react": "^0.4.22",
3638
"@kubernetes/client-node": "^0.20.0",
3739
"@material-ui/core": "^4.9.13",
3840
"@material-ui/icons": "^4.9.1",
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import React from 'react';
2+
3+
import { Alert, AlertTitle } from '@material-ui/lab';
4+
5+
const PermissionAlert = () => {
6+
return (
7+
<Alert severity="warning" data-testid="no-permission-alert">
8+
<AlertTitle>Permission required</AlertTitle>
9+
To view argocd plugin, contact your administrator to give you the
10+
argocd.view.read permission.
11+
</Alert>
12+
);
13+
};
14+
export default PermissionAlert;

plugins/argocd/src/components/DeploymentLifeCycle/DeploymentLifecycle.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ import { createStyles, makeStyles, Theme, Typography } from '@material-ui/core';
99
import { argoCDApiRef } from '../../api';
1010
import { useApplications } from '../../hooks/useApplications';
1111
import { useArgocdConfig } from '../../hooks/useArgocdConfig';
12+
import { useArgocdViewPermission } from '../../hooks/useArgocdViewPermission';
1213
import { Application, Revision } from '../../types';
1314
import {
1415
getAppSelector,
1516
getInstanceName,
1617
getUniqueRevisions,
1718
} from '../../utils/utils';
19+
import PermissionAlert from '../Common/PermissionAlert';
1820
import DeploymentLifecycleCard from './DeploymentLifecycleCard';
1921
import DeploymentLifecycleDrawer from './DeploymentLifecycleDrawer';
2022

@@ -50,6 +52,8 @@ const DeploymentLifecycle = () => {
5052
appSelector: encodeURIComponent(getAppSelector(entity)),
5153
});
5254

55+
const hasArgocdViewAccess = useArgocdViewPermission();
56+
5357
const [open, setOpen] = React.useState(false);
5458
const [activeItem, setActiveItem] = React.useState<string>();
5559
const [, setRevisions] = React.useState<{
@@ -87,6 +91,10 @@ const DeploymentLifecycle = () => {
8791

8892
const activeApp = apps.find(a => a.metadata.name === activeItem);
8993

94+
if (!hasArgocdViewAccess) {
95+
return <PermissionAlert />;
96+
}
97+
9098
if (error) {
9199
return <ResponseErrorPanel data-testid="error-panel" error={error} />;
92100
}

plugins/argocd/src/components/DeploymentLifeCycle/__tests__/DeploymentLifecycle.test.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { PropsWithChildren } from 'react';
22

33
import { useApi } from '@backstage/core-plugin-api';
4+
import { usePermission } from '@backstage/plugin-permission-react';
45

56
import { createTheme, ThemeProvider } from '@material-ui/core';
67
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
@@ -12,6 +13,13 @@ import DeploymentLifecycle from '../DeploymentLifecycle';
1213
jest.mock('../../../hooks/useArgocdConfig', () => ({
1314
useArgocdConfig: jest.fn(),
1415
}));
16+
jest.mock('@backstage/plugin-permission-react', () => ({
17+
usePermission: jest.fn(),
18+
}));
19+
20+
const mockUsePermission = usePermission as jest.MockedFunction<
21+
typeof usePermission
22+
>;
1523

1624
jest.mock('@backstage/core-plugin-api', () => ({
1725
...jest.requireActual('@backstage/core-plugin-api'),
@@ -33,6 +41,8 @@ describe('DeploymentLifecycle', () => {
3341
beforeEach(() => {
3442
jest.clearAllMocks();
3543

44+
mockUsePermission.mockReturnValue({ loading: false, allowed: true });
45+
3646
(useArgocdConfig as any).mockReturnValue({
3747
baseUrl: 'https://baseurl.com',
3848
instances: [{ name: 'main', url: 'https://main-instance-url.com' }],
@@ -55,6 +65,12 @@ describe('DeploymentLifecycle', () => {
5565
jest.spyOn(console, 'warn').mockImplementation(() => {});
5666
});
5767

68+
it('should render Permission alert if the user does not have view permission', () => {
69+
mockUsePermission.mockReturnValue({ loading: false, allowed: false });
70+
const { getByTestId } = render(<DeploymentLifecycle />);
71+
expect(getByTestId('no-permission-alert')).toBeInTheDocument();
72+
});
73+
5874
test('should render the loader component', async () => {
5975
render(<DeploymentLifecycle />);
6076

plugins/argocd/src/components/DeploymentSummary/DeploymentSummary.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import moment from 'moment';
99

1010
import { useApplications } from '../../hooks/useApplications';
1111
import { useArgocdConfig } from '../../hooks/useArgocdConfig';
12+
import { useArgocdViewPermission } from '../../hooks/useArgocdViewPermission';
1213
import { Application, HealthStatus, SyncStatuses } from '../../types';
1314
import {
1415
getAppSelector,
@@ -32,6 +33,8 @@ const DeploymentSummary = () => {
3233
projectName: getProjectName(entity),
3334
});
3435

36+
const hasArgocdViewAccess = useArgocdViewPermission();
37+
3538
const supportsMultipleArgoInstances = !!instances.length;
3639
const getBaseUrl = (row: any): string | undefined => {
3740
if (supportsMultipleArgoInstances && !baseUrl) {
@@ -162,7 +165,7 @@ const DeploymentSummary = () => {
162165
},
163166
];
164167

165-
return !error ? (
168+
return !error && hasArgocdViewAccess ? (
166169
<Table
167170
title="Deployment summary"
168171
options={{

plugins/argocd/src/components/DeploymentSummary/__tests__/DeploymentSummary.test.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import React from 'react';
22

3+
import { usePermission } from '@backstage/plugin-permission-react';
4+
35
import {
46
fireEvent,
57
render,
@@ -22,6 +24,14 @@ jest.mock('../../../hooks/useApplications', () => ({
2224
useApplications: jest.fn(),
2325
}));
2426

27+
jest.mock('@backstage/plugin-permission-react', () => ({
28+
usePermission: jest.fn(),
29+
}));
30+
31+
const mockUsePermission = usePermission as jest.MockedFunction<
32+
typeof usePermission
33+
>;
34+
2535
jest.mock('@backstage/plugin-catalog-react', () => ({
2636
useEntity: () => mockEntity,
2737
}));
@@ -34,6 +44,8 @@ describe('DeploymentSummary', () => {
3444
error: undefined,
3545
});
3646

47+
mockUsePermission.mockReturnValue({ loading: false, allowed: true });
48+
3749
(useArgocdConfig as any).mockReturnValue({
3850
baseUrl: '',
3951
instances: [{ name: 'main', url: 'https://main-instance-url.com' }],
@@ -76,6 +88,15 @@ describe('DeploymentSummary', () => {
7688
expect(screen.queryByText('No records to display')).toBeInTheDocument();
7789
});
7890
});
91+
test('should not render deployment summary table when the user does not have view permission', async () => {
92+
mockUsePermission.mockReturnValue({ loading: false, allowed: false });
93+
94+
render(<DeploymentSummary />);
95+
96+
await waitFor(() => {
97+
expect(screen.queryByText('Deployment summary')).not.toBeInTheDocument();
98+
});
99+
});
79100

80101
test('should not render deployment summary table incase of error', async () => {
81102
(useApplications as any).mockReturnValue({
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { usePermission } from '@backstage/plugin-permission-react';
2+
3+
import { argocdViewPermission } from '@janus-idp/backstage-plugin-argocd-common';
4+
5+
export const useArgocdViewPermission = () => {
6+
const argocdViewPermissionResult = usePermission({
7+
permission: argocdViewPermission,
8+
});
9+
10+
return argocdViewPermissionResult.allowed;
11+
};

plugins/argocd/src/plugin.test.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ import { Route, Routes } from 'react-router-dom';
33

44
import { configApiRef } from '@backstage/core-plugin-api';
55
import { EntityProvider } from '@backstage/plugin-catalog-react';
6+
import { permissionApiRef } from '@backstage/plugin-permission-react';
67
import {
78
MockConfigApi,
9+
MockPermissionApi,
810
renderInTestApp,
911
TestApiProvider,
1012
} from '@backstage/test-utils';
@@ -33,6 +35,8 @@ describe('argocd', () => {
3335
},
3436
};
3537

38+
const mockPermissionApi = new MockPermissionApi();
39+
3640
const mockConfiguration = new MockConfigApi({
3741
backend: {
3842
baseUrl: 'http://localhost:7007',
@@ -50,6 +54,7 @@ describe('argocd', () => {
5054
apis={[
5155
[argoCDApiRef, mockedApi],
5256
[configApiRef, mockConfiguration],
57+
[permissionApiRef, mockPermissionApi],
5358
]}
5459
>
5560
<EntityProvider entity={mockEntity}>{children}</EntityProvider>

plugins/rbac-backend/docs/permissions.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,11 @@ Resource type permissions on the other hand are basic named permissions with a r
119119
| ------------------ | ------------- | ------ | ----------------------------------------------------------------------------------------------------------- | ------------------- |
120120
| topology.view.read | | read | Allows the user to view the topology plugin | X |
121121
| kubernetes.proxy | | | Allows the user to access the proxy endpoint (ability to read pod logs and events within Showcase and RHDH) | catalog.entity.read |
122+
123+
## Argocd
124+
125+
| Name | Resource Type | Policy | Description | Requirements |
126+
127+
| -------------- | ------------- | ------ | --------------------------------------- | ------------------- |
128+
129+
| argocd.view.read | | read | Allows the user to view the argocd plugin | catalog.entity.read |

0 commit comments

Comments
 (0)