From 15686b209aa7bba936a4dd482d59458cb49ab462 Mon Sep 17 00:00:00 2001
From: Carlos Esteban Feria Vila
<2582866+carlosthe19916@users.noreply.github.com>
Date: Mon, 1 Nov 2021 09:48:07 +0100
Subject: [PATCH 1/9] save changes
---
README.md | 6 +-
docker-compose.yml | 24 +--
konveyor-realm.json | 359 +++++++++++++++++++++++++++-----------------
src/setupProxy.js | 6 +-
4 files changed, 242 insertions(+), 153 deletions(-)
diff --git a/README.md b/README.md
index 052c7b44..0411ea50 100644
--- a/README.md
+++ b/README.md
@@ -63,7 +63,7 @@ Start a database which will be used by `tackle-controls`:
docker run -d -p 5432:5432 \
-e POSTGRES_USER=username \
-e POSTGRES_PASSWORD=password \
--e POSTGRES_DB=controls_db \
+-e POSTGRES_DB=db \
postgres:13.1
```
@@ -76,8 +76,8 @@ Move your terminal to where you cloned `tackle-controls` and then:
-Dquarkus.http.port=8080 \
-Dquarkus.datasource.username=username \
-Dquarkus.datasource.password=password \
--Dquarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/controls_db \
--Dquarkus.oidc.client-id=controls-api \
+-Dquarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/db \
+-Dquarkus.oidc.client-id=tackle-api \
-Dquarkus.oidc.credentials.secret=secret \
-Dquarkus.oidc.auth-server-url=http://localhost:8180/auth/realms/konveyor
```
diff --git a/docker-compose.yml b/docker-compose.yml
index be0727d4..df2b450d 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -23,11 +23,11 @@ services:
ports:
- 5433:5432
environment:
- POSTGRES_DB: controls_db
+ POSTGRES_DB: db
POSTGRES_USER: user
POSTGRES_PASSWORD: password
healthcheck:
- test: ["CMD-SHELL", "pg_isready -U user -d controls_db"]
+ test: ["CMD-SHELL", "pg_isready -U user -d db"]
interval: 10s
timeout: 5s
retries: 5
@@ -40,9 +40,9 @@ services:
QUARKUS_HTTP_PORT: 8080
QUARKUS_DATASOURCE_USERNAME: user
QUARKUS_DATASOURCE_PASSWORD: password
- QUARKUS_DATASOURCE_JDBC_URL: jdbc:postgresql://controls-db:5432/controls_db
+ QUARKUS_DATASOURCE_JDBC_URL: jdbc:postgresql://controls-db:5432/db
QUARKUS_OIDC_AUTH_SERVER_URL: http://keycloak:8080/auth/realms/konveyor
- QUARKUS_OIDC_CLIENT_ID: controls-api
+ QUARKUS_OIDC_CLIENT_ID: tackle-api
QUARKUS_OIDC_CREDENTIALS_SECRET: secret
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/controls/q/health"]
@@ -60,11 +60,11 @@ services:
ports:
- 5434:5432
environment:
- POSTGRES_DB: application_inventory_db
+ POSTGRES_DB: db
POSTGRES_USER: user
POSTGRES_PASSWORD: password
healthcheck:
- test: ["CMD-SHELL", "pg_isready -U user -d application_inventory_db"]
+ test: ["CMD-SHELL", "pg_isready -U user -d db"]
interval: 10s
timeout: 5s
retries: 5
@@ -77,9 +77,9 @@ services:
QUARKUS_HTTP_PORT: 8080
QUARKUS_DATASOURCE_USERNAME: user
QUARKUS_DATASOURCE_PASSWORD: password
- QUARKUS_DATASOURCE_JDBC_URL: jdbc:postgresql://application-inventory-db:5432/application_inventory_db
+ QUARKUS_DATASOURCE_JDBC_URL: jdbc:postgresql://application-inventory-db:5432/db
QUARKUS_OIDC_AUTH_SERVER_URL: http://keycloak:8080/auth/realms/konveyor
- QUARKUS_OIDC_CLIENT_ID: application-inventory-api
+ QUARKUS_OIDC_CLIENT_ID: tackle-api
QUARKUS_OIDC_CREDENTIALS_SECRET: secret
IO_TACKLE_APPLICATIONINVENTORY_SERVICES_CONTROLS_SERVICE: controls:8080
healthcheck:
@@ -104,11 +104,11 @@ services:
ports:
- 5435:5432
environment:
- POSTGRES_DB: pathfinder_db
+ POSTGRES_DB: db
POSTGRES_USER: user
POSTGRES_PASSWORD: password
healthcheck:
- test: ["CMD-SHELL", "pg_isready -U user -d pathfinder_db"]
+ test: ["CMD-SHELL", "pg_isready -U user -d db"]
interval: 10s
timeout: 5s
retries: 5
@@ -121,9 +121,9 @@ services:
QUARKUS_HTTP_PORT: 8080
QUARKUS_DATASOURCE_USERNAME: user
QUARKUS_DATASOURCE_PASSWORD: password
- QUARKUS_DATASOURCE_JDBC_URL: jdbc:postgresql://pathfinder-db:5432/pathfinder_db
+ QUARKUS_DATASOURCE_JDBC_URL: jdbc:postgresql://pathfinder-db:5432/db
QUARKUS_OIDC_AUTH_SERVER_URL: http://keycloak:8080/auth/realms/konveyor
- QUARKUS_OIDC_CLIENT_ID: pathfinder-api
+ QUARKUS_OIDC_CLIENT_ID: tackle-api
QUARKUS_OIDC_CREDENTIALS_SECRET: secret
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/pathfinder/q/health"]
diff --git a/konveyor-realm.json b/konveyor-realm.json
index 171d99f6..8c8b3620 100644
--- a/konveyor-realm.json
+++ b/konveyor-realm.json
@@ -45,15 +45,81 @@
{
"id": "d723b5ff-6c33-4152-b0ac-3ad9c1b79e6c",
"name": "admin",
- "composite": false,
+ "description": "Admins of Tackle",
+ "composite": true,
+ "composites": {
+ "client": {
+ "tackle-api": [
+ "tag:write",
+ "business-metadata:write",
+ "application:write",
+ "application-import:write",
+ "application-assessment:write",
+ "application-dependency:write"
+ ]
+ }
+ },
+ "clientRole": false,
+ "containerId": "konveyor",
+ "attributes": {}
+ },
+ {
+ "id": "f777a295-e6bc-45d5-8d84-e476a9021242",
+ "name": "architect",
+ "description": "Architects of Tackle",
+ "composite": true,
+ "composites": {
+ "client": {
+ "tackle-api": [
+ "tag:write",
+ "business-metadata:write",
+ "application:write",
+ "application-import:write",
+ "application-assessment:write",
+ "application-dependency:write"
+ ]
+ }
+ },
+ "clientRole": false,
+ "containerId": "konveyor",
+ "attributes": {}
+ },
+ {
+ "id": "014309e1-d2cf-496f-9cc3-fb156c86e360",
+ "name": "migrator",
+ "description": "Migrators of Tackle",
+ "composite": true,
+ "composites": {
+ "client": {
+ "tackle-api": [
+ "tag:read",
+ "business-metadata:read",
+ "application-import:read",
+ "application-assessment:write",
+ "application:read"
+ ]
+ }
+ },
"clientRole": false,
"containerId": "konveyor",
"attributes": {}
},
{
- "id": "88607edb-72b0-46fb-8d76-1a75a51a50f0",
+ "id": "74c6b736-7934-44de-88c0-435c927ada01",
"name": "user",
- "composite": false,
+ "description": "User of Tackle",
+ "composite": true,
+ "composites": {
+ "client": {
+ "tackle-api": [
+ "tag:read",
+ "application-import:read",
+ "application-assessment:read",
+ "business-metadata:read",
+ "application:read"
+ ]
+ }
+ },
"clientRole": false,
"containerId": "konveyor",
"attributes": {}
@@ -291,7 +357,7 @@
}
],
"security-admin-console": [],
- "controls-api": [
+ "tackle-api": [
{
"id": "d056105b-2d60-4415-addb-9639ac3bfd74",
"name": "uma_protection",
@@ -299,6 +365,153 @@
"clientRole": true,
"containerId": "5cda179e-8443-4467-a488-292976d25bf1",
"attributes": {}
+ },
+ {
+ "id": "f89d033f-1d90-4061-bfff-bf380aaa844a",
+ "name": "application:write",
+ "composite": true,
+ "composites": {
+ "client": {
+ "tackle-api": [
+ "application:read"
+ ]
+ }
+ },
+ "clientRole": true,
+ "containerId": "a19ae9a3-c9c1-4f4a-b39a-1bd5851eee90",
+ "attributes": {}
+ },
+ {
+ "id": "67082935-a948-4395-a0c8-a851773ca1ba",
+ "name": "application-import:write",
+ "composite": true,
+ "composites": {
+ "client": {
+ "tackle-api": [
+ "application-import:read"
+ ]
+ }
+ },
+ "clientRole": true,
+ "containerId": "a19ae9a3-c9c1-4f4a-b39a-1bd5851eee90",
+ "attributes": {}
+ },
+ {
+ "id": "caf1d234-b5e4-4cbe-8915-24a9a7cc7ab1",
+ "name": "application-assessment:read",
+ "composite": false,
+ "clientRole": true,
+ "containerId": "a19ae9a3-c9c1-4f4a-b39a-1bd5851eee90",
+ "attributes": {}
+ },
+ {
+ "id": "691daa33-980e-419a-a63f-c86d07a03dae",
+ "name": "tag:read",
+ "composite": false,
+ "clientRole": true,
+ "containerId": "a19ae9a3-c9c1-4f4a-b39a-1bd5851eee90",
+ "attributes": {}
+ },
+ {
+ "id": "f0eb63c2-033f-447a-85bd-55c83f1e3619",
+ "name": "application-import:read",
+ "composite": true,
+ "composites": {
+ "client": {
+ "tackle-api": [
+ "application:read"
+ ]
+ }
+ },
+ "clientRole": true,
+ "containerId": "a19ae9a3-c9c1-4f4a-b39a-1bd5851eee90",
+ "attributes": {}
+ },
+ {
+ "id": "1045e9bb-130f-4bc9-bf35-f50d8e46722e",
+ "name": "application-assessment:write",
+ "composite": true,
+ "composites": {
+ "client": {
+ "tackle-api": [
+ "application-assessment:read",
+ "application:read"
+ ]
+ }
+ },
+ "clientRole": true,
+ "containerId": "a19ae9a3-c9c1-4f4a-b39a-1bd5851eee90",
+ "attributes": {}
+ },
+ {
+ "id": "5cf0b4ca-7e26-4f86-a3d3-6dab69243d33",
+ "name": "application:read",
+ "composite": true,
+ "composites": {
+ "client": {
+ "tackle-api": [
+ "tag:read",
+ "application-assessment:read",
+ "business-metadata:read"
+ ]
+ }
+ },
+ "clientRole": true,
+ "containerId": "a19ae9a3-c9c1-4f4a-b39a-1bd5851eee90",
+ "attributes": {}
+ },
+ {
+ "id": "3ed86dfd-d354-4d70-9467-a0b8270bb37c",
+ "name": "business-metadata:read",
+ "composite": false,
+ "clientRole": true,
+ "containerId": "a19ae9a3-c9c1-4f4a-b39a-1bd5851eee90",
+ "attributes": {}
+ },
+ {
+ "id": "574e82d1-9fc3-4166-bccd-f227afe02982",
+ "name": "tag:write",
+ "composite": true,
+ "composites": {
+ "client": {
+ "tackle-api": [
+ "tag:read"
+ ]
+ }
+ },
+ "clientRole": true,
+ "containerId": "a19ae9a3-c9c1-4f4a-b39a-1bd5851eee90",
+ "attributes": {}
+ },
+ {
+ "id": "d8a18018-8ce5-497f-b7e3-de7b1112ac39",
+ "name": "application-dependency:write",
+ "composite": true,
+ "composites": {
+ "client": {
+ "tackle-api": [
+ "application:read"
+ ]
+ }
+ },
+ "clientRole": true,
+ "containerId": "a19ae9a3-c9c1-4f4a-b39a-1bd5851eee90",
+ "attributes": {}
+ },
+ {
+ "id": "703d2149-4c71-47e9-922e-8643ffbff834",
+ "name": "business-metadata:write",
+ "composite": true,
+ "composites": {
+ "client": {
+ "tackle-api": [
+ "business-metadata:read"
+ ]
+ }
+ },
+ "clientRole": true,
+ "containerId": "a19ae9a3-c9c1-4f4a-b39a-1bd5851eee90",
+ "attributes": {}
}
],
"admin-cli": [],
@@ -389,7 +602,8 @@
"groups": [],
"defaultRoles": [
"uma_authorization",
- "offline_access"
+ "offline_access",
+ "user"
],
"requiredCredentials": [
"password"
@@ -454,8 +668,7 @@
],
"requiredActions": [],
"realmRoles": [
- "admin",
- "user"
+ "admin"
],
"notBefore": 0,
"groups": []
@@ -493,11 +706,11 @@
{
"id": "f2f78ee5-b6f9-4e7b-a837-8301a30a6d73",
"createdTimestamp": 1602936578977,
- "username": "service-account-controls-api",
+ "username": "service-account-tackle-api",
"enabled": true,
"totp": false,
"emailVerified": false,
- "serviceAccountClientId": "controls-api",
+ "serviceAccountClientId": "tackle-api",
"disableableCredentialTypes": [],
"requiredActions": [],
"realmRoles": [
@@ -505,7 +718,7 @@
"offline_access"
],
"clientRoles": {
- "controls-api": [
+ "tackle-api": [
"uma_protection"
],
"account": [
@@ -821,7 +1034,7 @@
},
{
"id": "5cda179e-8443-4467-a488-292976d25bf1",
- "clientId": "controls-api",
+ "clientId": "tackle-api",
"surrogateAuthRequired": false,
"enabled": true,
"alwaysDisplayInConsole": false,
@@ -881,130 +1094,6 @@
"manage": true
}
},
- {
- "id": "065c4bcf-379e-4a83-99d7-5491176185e2",
- "clientId": "application-inventory-api",
- "surrogateAuthRequired": false,
- "enabled": true,
- "alwaysDisplayInConsole": false,
- "clientAuthenticatorType": "client-secret",
- "secret": "secret",
- "redirectUris": [
- "/*"
- ],
- "webOrigins": [],
- "notBefore": 0,
- "bearerOnly": false,
- "consentRequired": false,
- "standardFlowEnabled": true,
- "implicitFlowEnabled": false,
- "directAccessGrantsEnabled": true,
- "serviceAccountsEnabled": false,
- "publicClient": false,
- "frontchannelLogout": false,
- "protocol": "openid-connect",
- "attributes": {
- "saml.assertion.signature": "false",
- "saml.force.post.binding": "false",
- "saml.multivalued.roles": "false",
- "saml.encrypt": "false",
- "backchannel.logout.revoke.offline.tokens": "false",
- "saml.server.signature": "false",
- "saml.server.signature.keyinfo.ext": "false",
- "exclude.session.state.from.auth.response": "false",
- "backchannel.logout.session.required": "true",
- "client_credentials.use_refresh_token": "false",
- "saml_force_name_id_format": "false",
- "saml.client.signature": "false",
- "tls.client.certificate.bound.access.tokens": "false",
- "saml.authnstatement": "false",
- "display.on.consent.screen": "false",
- "saml.onetimeuse.condition": "false"
- },
- "authenticationFlowBindingOverrides": {},
- "fullScopeAllowed": true,
- "nodeReRegistrationTimeout": -1,
- "defaultClientScopes": [
- "web-origins",
- "role_list",
- "roles",
- "profile",
- "email"
- ],
- "optionalClientScopes": [
- "address",
- "phone",
- "offline_access",
- "microprofile-jwt"
- ],
- "access": {
- "view": true,
- "configure": true,
- "manage": true
- }
- },
- {
- "id": "7f4a9ed7-3554-4aef-955a-a5737fb942f3",
- "clientId": "pathfinder-api",
- "surrogateAuthRequired": false,
- "enabled": true,
- "alwaysDisplayInConsole": false,
- "clientAuthenticatorType": "client-secret",
- "secret": "secret",
- "redirectUris": [
- "/*"
- ],
- "webOrigins": [],
- "notBefore": 0,
- "bearerOnly": false,
- "consentRequired": false,
- "standardFlowEnabled": true,
- "implicitFlowEnabled": false,
- "directAccessGrantsEnabled": true,
- "serviceAccountsEnabled": false,
- "publicClient": false,
- "frontchannelLogout": false,
- "protocol": "openid-connect",
- "attributes": {
- "saml.assertion.signature": "false",
- "saml.force.post.binding": "false",
- "saml.multivalued.roles": "false",
- "saml.encrypt": "false",
- "backchannel.logout.revoke.offline.tokens": "false",
- "saml.server.signature": "false",
- "saml.server.signature.keyinfo.ext": "false",
- "exclude.session.state.from.auth.response": "false",
- "backchannel.logout.session.required": "true",
- "client_credentials.use_refresh_token": "false",
- "saml_force_name_id_format": "false",
- "saml.client.signature": "false",
- "tls.client.certificate.bound.access.tokens": "false",
- "saml.authnstatement": "false",
- "display.on.consent.screen": "false",
- "saml.onetimeuse.condition": "false"
- },
- "authenticationFlowBindingOverrides": {},
- "fullScopeAllowed": true,
- "nodeReRegistrationTimeout": -1,
- "defaultClientScopes": [
- "web-origins",
- "role_list",
- "roles",
- "profile",
- "email"
- ],
- "optionalClientScopes": [
- "address",
- "phone",
- "offline_access",
- "microprofile-jwt"
- ],
- "access": {
- "view": true,
- "configure": true,
- "manage": true
- }
- },
{
"id": "695da74e-39ca-4e46-a2b6-6a673f92d4e2",
"clientId": "tackle-ui",
diff --git a/src/setupProxy.js b/src/setupProxy.js
index 76f4730f..e403b9ad 100644
--- a/src/setupProxy.js
+++ b/src/setupProxy.js
@@ -4,7 +4,7 @@ module.exports = function (app) {
app.use(
"/api/controls",
createProxyMiddleware({
- target: "http://localhost:8081",
+ target: "http://localhost:8087",
changeOrigin: true,
pathRewrite: {
"^/api/controls": "/controls",
@@ -15,7 +15,7 @@ module.exports = function (app) {
app.use(
"/api/application-inventory",
createProxyMiddleware({
- target: "http://localhost:8082",
+ target: "http://localhost:8088",
changeOrigin: true,
pathRewrite: {
"^/api/application-inventory": "/application-inventory",
@@ -26,7 +26,7 @@ module.exports = function (app) {
app.use(
"/api/pathfinder",
createProxyMiddleware({
- target: "http://localhost:8083",
+ target: "http://localhost:8089",
changeOrigin: true,
pathRewrite: {
"^/api/pathfinder": "/pathfinder",
From 5e2821622a785a87a08ec0a1366c714f53fea045 Mon Sep 17 00:00:00 2001
From: Carlos Esteban Feria Vila
<2582866+carlosthe19916@users.noreply.github.com>
Date: Mon, 1 Nov 2021 14:53:06 +0100
Subject: [PATCH 2/9] Set RBAC for showing and hidding elements
---
konveyor-realm.json | 138 ++++++-------
src/Constants.ts | 16 ++
src/layout/SidebarApp/SidebarApp.tsx | 54 +++--
.../application-list/application-list.tsx | 185 +++++++++++-------
.../business-services/business-services.tsx | 51 +++--
.../controls/job-functions/job-functions.tsx | 53 +++--
.../stakeholder-groups/stakeholder-groups.tsx | 51 +++--
.../controls/stakeholders/stakeholders.tsx | 51 +++--
.../tags/components/tag-table/tag-table.tsx | 10 +-
src/pages/controls/tags/tags.tsx | 80 +++++---
src/shared/components/index.ts | 1 +
.../visibility-by-permission/index.ts | 1 +
.../visibility-by-permission.tsx | 15 ++
src/shared/hooks/index.ts | 1 +
src/shared/hooks/useKcPermission/index.ts | 1 +
.../hooks/useKcPermission/useKcPermission.ts | 27 +++
src/utils/utils.ts | 6 +
17 files changed, 473 insertions(+), 268 deletions(-)
create mode 100644 src/shared/components/visibility-by-permission/index.ts
create mode 100644 src/shared/components/visibility-by-permission/visibility-by-permission.tsx
create mode 100644 src/shared/hooks/useKcPermission/index.ts
create mode 100644 src/shared/hooks/useKcPermission/useKcPermission.ts
diff --git a/konveyor-realm.json b/konveyor-realm.json
index 8c8b3620..70bd99f0 100644
--- a/konveyor-realm.json
+++ b/konveyor-realm.json
@@ -50,19 +50,19 @@
"composites": {
"client": {
"tackle-api": [
- "tag:write",
- "business-metadata:write",
- "application:write",
- "application-import:write",
- "application-assessment:write",
- "application-dependency:write"
+ "controls:write",
+ "inventory:application:write",
+ "inventory:application-import:write",
+ "inventory:application-dependency:write",
+ "inventory:application-review:write",
+ "pathfinder:assessment:write"
]
}
},
"clientRole": false,
"containerId": "konveyor",
"attributes": {}
- },
+ },
{
"id": "f777a295-e6bc-45d5-8d84-e476a9021242",
"name": "architect",
@@ -71,12 +71,12 @@
"composites": {
"client": {
"tackle-api": [
- "tag:write",
- "business-metadata:write",
- "application:write",
- "application-import:write",
- "application-assessment:write",
- "application-dependency:write"
+ "controls:write",
+ "inventory:application:write",
+ "inventory:application-import:write",
+ "inventory:application-dependency:write",
+ "inventory:application-review:write",
+ "pathfinder:assessment:write"
]
}
},
@@ -92,11 +92,10 @@
"composites": {
"client": {
"tackle-api": [
- "tag:read",
- "business-metadata:read",
- "application-import:read",
- "application-assessment:write",
- "application:read"
+ "controls:read",
+ "inventory:application:read",
+ "inventory:application-import:read",
+ "pathfinder:assessment:write"
]
}
},
@@ -112,11 +111,10 @@
"composites": {
"client": {
"tackle-api": [
- "tag:read",
- "application-import:read",
- "application-assessment:read",
- "business-metadata:read",
- "application:read"
+ "controls:read",
+ "inventory:application:read",
+ "inventory:application-import:read",
+ "pathfinder:assessment:read"
]
}
},
@@ -367,13 +365,21 @@
"attributes": {}
},
{
- "id": "f89d033f-1d90-4061-bfff-bf380aaa844a",
- "name": "application:write",
+ "id": "691daa33-980e-419a-a63f-c86d07a03dae",
+ "name": "controls:read",
+ "composite": false,
+ "clientRole": true,
+ "containerId": "a19ae9a3-c9c1-4f4a-b39a-1bd5851eee90",
+ "attributes": {}
+ },
+ {
+ "id": "574e82d1-9fc3-4166-bccd-f227afe02982",
+ "name": "controls:write",
"composite": true,
"composites": {
"client": {
"tackle-api": [
- "application:read"
+ "controls:read"
]
}
},
@@ -382,13 +388,14 @@
"attributes": {}
},
{
- "id": "67082935-a948-4395-a0c8-a851773ca1ba",
- "name": "application-import:write",
+ "id": "5cf0b4ca-7e26-4f86-a3d3-6dab69243d33",
+ "name": "inventory:application:read",
"composite": true,
"composites": {
"client": {
"tackle-api": [
- "application-import:read"
+ "controls:read",
+ "pathfinder:assessment:read"
]
}
},
@@ -397,29 +404,13 @@
"attributes": {}
},
{
- "id": "caf1d234-b5e4-4cbe-8915-24a9a7cc7ab1",
- "name": "application-assessment:read",
- "composite": false,
- "clientRole": true,
- "containerId": "a19ae9a3-c9c1-4f4a-b39a-1bd5851eee90",
- "attributes": {}
- },
- {
- "id": "691daa33-980e-419a-a63f-c86d07a03dae",
- "name": "tag:read",
- "composite": false,
- "clientRole": true,
- "containerId": "a19ae9a3-c9c1-4f4a-b39a-1bd5851eee90",
- "attributes": {}
- },
- {
- "id": "f0eb63c2-033f-447a-85bd-55c83f1e3619",
- "name": "application-import:read",
+ "id": "f89d033f-1d90-4061-bfff-bf380aaa844a",
+ "name": "inventory:application:write",
"composite": true,
"composites": {
"client": {
"tackle-api": [
- "application:read"
+ "inventory:application:read"
]
}
},
@@ -428,14 +419,13 @@
"attributes": {}
},
{
- "id": "1045e9bb-130f-4bc9-bf35-f50d8e46722e",
- "name": "application-assessment:write",
+ "id": "f0eb63c2-033f-447a-85bd-55c83f1e3619",
+ "name": "inventory:application-import:read",
"composite": true,
"composites": {
"client": {
"tackle-api": [
- "application-assessment:read",
- "application:read"
+ "inventory:application:read"
]
}
},
@@ -444,15 +434,13 @@
"attributes": {}
},
{
- "id": "5cf0b4ca-7e26-4f86-a3d3-6dab69243d33",
- "name": "application:read",
+ "id": "67082935-a948-4395-a0c8-a851773ca1ba",
+ "name": "inventory:application-import:write",
"composite": true,
"composites": {
"client": {
"tackle-api": [
- "tag:read",
- "application-assessment:read",
- "business-metadata:read"
+ "inventory:application-import:read"
]
}
},
@@ -461,21 +449,13 @@
"attributes": {}
},
{
- "id": "3ed86dfd-d354-4d70-9467-a0b8270bb37c",
- "name": "business-metadata:read",
- "composite": false,
- "clientRole": true,
- "containerId": "a19ae9a3-c9c1-4f4a-b39a-1bd5851eee90",
- "attributes": {}
- },
- {
- "id": "574e82d1-9fc3-4166-bccd-f227afe02982",
- "name": "tag:write",
+ "id": "d8a18018-8ce5-497f-b7e3-de7b1112ac39",
+ "name": "inventory:application-dependency:write",
"composite": true,
"composites": {
"client": {
"tackle-api": [
- "tag:read"
+ "inventory:application:read"
]
}
},
@@ -484,13 +464,14 @@
"attributes": {}
},
{
- "id": "d8a18018-8ce5-497f-b7e3-de7b1112ac39",
- "name": "application-dependency:write",
+ "id": "3ed86dfd-d354-4d70-9467-a0b8270bb37c",
+ "name": "inventory:application-review:write",
"composite": true,
"composites": {
"client": {
"tackle-api": [
- "application:read"
+ "inventory:application:read",
+ "pathfinder:assessment:write"
]
}
},
@@ -499,13 +480,22 @@
"attributes": {}
},
{
- "id": "703d2149-4c71-47e9-922e-8643ffbff834",
- "name": "business-metadata:write",
+ "id": "caf1d234-b5e4-4cbe-8915-24a9a7cc7ab1",
+ "name": "pathfinder:assessment:read",
+ "composite": false,
+ "clientRole": true,
+ "containerId": "a19ae9a3-c9c1-4f4a-b39a-1bd5851eee90",
+ "attributes": {}
+ },
+ {
+ "id": "03f48fd4-de69-4baa-8177-cdab92d76209",
+ "name": "pathfinder:assessment:write",
"composite": true,
"composites": {
"client": {
"tackle-api": [
- "business-metadata:read"
+ "inventory:application:read",
+ "pathfinder:assessment:read"
]
}
},
diff --git a/src/Constants.ts b/src/Constants.ts
index 15f6d256..c45369ac 100644
--- a/src/Constants.ts
+++ b/src/Constants.ts
@@ -194,3 +194,19 @@ export enum ApplicationFilterKey {
BUSINESS_SERVICE = "business_service",
TAG = "tag",
}
+
+// Keycloak RBAC
+
+export const KC_API_CLIENT = "tackle-api";
+
+export type KcPermission =
+ | "controls:read"
+ | "controls:write"
+ | "inventory:application:read"
+ | "inventory:application:write"
+ | "inventory:application-import:read"
+ | "inventory:application-import:write"
+ | "inventory:application-dependency:write"
+ | "inventory:application-review:write"
+ | "pathfinder:assessment:read"
+ | "pathfinder:assessment:write";
diff --git a/src/layout/SidebarApp/SidebarApp.tsx b/src/layout/SidebarApp/SidebarApp.tsx
index 02d63419..0b5f7c44 100644
--- a/src/layout/SidebarApp/SidebarApp.tsx
+++ b/src/layout/SidebarApp/SidebarApp.tsx
@@ -5,33 +5,51 @@ import { Nav, NavItem, PageSidebar, NavList } from "@patternfly/react-core";
import { Paths } from "Paths";
import { LayoutTheme } from "../LayoutUtils";
+import { useKcPermission } from "shared/hooks";
+import { VisibilityByPermission } from "shared/components";
export const SidebarApp: React.FC = () => {
+ // i18
const { t } = useTranslation();
+
+ // Location
const { search } = useLocation();
const renderPageNav = () => {
return (
);
diff --git a/src/pages/application-inventory/application-list/application-list.tsx b/src/pages/application-inventory/application-list/application-list.tsx
index 2265ed82..d9056bf6 100644
--- a/src/pages/application-inventory/application-list/application-list.tsx
+++ b/src/pages/application-inventory/application-list/application-list.tsx
@@ -45,6 +45,7 @@ import {
NoDataEmptyState,
StatusIconAssessment,
KebabDropdown,
+ VisibilityByPermission,
} from "shared/components";
import {
useTableControls,
@@ -54,6 +55,7 @@ import {
useEntityModal,
useDelete,
useApplicationToolbarFilter,
+ useKcPermission,
} from "shared/hooks";
import { ApplicationDependenciesFormContainer } from "shared/containers";
@@ -76,7 +78,7 @@ import {
getAssessments,
} from "api/rest";
import { applicationPageMapper } from "api/apiUtils";
-import { getAxiosErrorMessage } from "utils/utils";
+import { getAxiosErrorMessage, wrapInArrayWhen } from "utils/utils";
import { ApplicationForm } from "./components/application-form";
@@ -139,6 +141,17 @@ export const ApplicationList: React.FC = () => {
// Router
const history = useHistory();
+ // RBAC
+ const { isAllowed: isAllowedToWriteApp } = useKcPermission({
+ permissionsAllowed: ["inventory:application:write"],
+ });
+ const { isAllowed: isAllowedToWriteAssessmentAndReview } = useKcPermission({
+ permissionsAllowed: ["inventory:application-review:write"],
+ });
+ const { isAllowed: isAllowedToWriteAppDependencies } = useKcPermission({
+ permissionsAllowed: ["inventory:application-dependency:write"],
+ });
+
// Toolbar filters
const {
filters: filtersValue,
@@ -291,12 +304,12 @@ export const ApplicationList: React.FC = () => {
{ title: t("terms.assessment"), transforms: [cellWidth(10)] },
{ title: t("terms.review"), transforms: [sortable, cellWidth(10)] },
{ title: t("terms.tagCount"), transforms: [sortable, cellWidth(10)] },
- {
+ ...wrapInArrayWhen(isAllowedToWriteApp, {
title: "",
props: {
className: "pf-c-table__inline-edit-action",
},
- },
+ }),
];
const rows: IRow[] = [];
@@ -353,7 +366,7 @@ export const ApplicationList: React.FC = () => {
>
),
},
- {
+ ...wrapInArrayWhen(isAllowedToWriteApp, {
title: (
),
- },
+ }),
],
});
@@ -388,7 +401,10 @@ export const ApplicationList: React.FC = () => {
const actions: (IAction | ISeparator)[] = [];
- if (getApplicationAssessment(row.id!)) {
+ if (
+ isAllowedToWriteAssessmentAndReview &&
+ getApplicationAssessment(row.id!)
+ ) {
actions.push({
title: t("actions.discardAssessment"),
onClick: (
@@ -402,8 +418,8 @@ export const ApplicationList: React.FC = () => {
});
}
- actions.push(
- {
+ if (isAllowedToWriteAppDependencies) {
+ actions.push({
title: t("actions.manageDependencies"),
onClick: (
event: React.MouseEvent,
@@ -413,8 +429,11 @@ export const ApplicationList: React.FC = () => {
const row: Application = getRow(rowData);
openDependenciesModal(row);
},
- },
- {
+ });
+ }
+
+ if (isAllowedToWriteApp) {
+ actions.push({
title: t("actions.delete"),
onClick: (
event: React.MouseEvent,
@@ -424,8 +443,8 @@ export const ApplicationList: React.FC = () => {
const row: Application = getRow(rowData);
deleteRow(row);
},
- }
- );
+ });
+ }
return actions;
};
@@ -653,68 +672,86 @@ export const ApplicationList: React.FC = () => {
toolbar={
<>
-
-
-
-
-
-
-
-
-
-
- setIsApplicationImportModalOpen(true)}
- >
- {t("actions.import")}
- ,
- {
- history.push(
- Paths.applicationInventory_manageImports
- );
- }}
- >
- {t("actions.manageImports")}
- ,
- ]}
- />
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ setIsApplicationImportModalOpen(true)
+ }
+ >
+ {t("actions.import")}
+ ,
+ {
+ history.push(
+ Paths.applicationInventory_manageImports
+ );
+ }}
+ >
+ {t("actions.manageImports")}
+ ,
+ ]}
+ />
+
+
>
}
diff --git a/src/pages/controls/business-services/business-services.tsx b/src/pages/controls/business-services/business-services.tsx
index c314814d..1f03fa6f 100644
--- a/src/pages/controls/business-services/business-services.tsx
+++ b/src/pages/controls/business-services/business-services.tsx
@@ -29,11 +29,13 @@ import {
AppTableActionButtons,
AppTableToolbarToggleGroup,
NoDataEmptyState,
+ VisibilityByPermission,
} from "shared/components";
import {
useTableControls,
useFetchBusinessServices,
useDelete,
+ useKcPermission,
} from "shared/hooks";
import { BusinessService, SortByQuery } from "api/models";
@@ -42,7 +44,7 @@ import {
BusinessServiceSortByQuery,
deleteBusinessService,
} from "api/rest";
-import { getAxiosErrorMessage } from "utils/utils";
+import { getAxiosErrorMessage, wrapInArrayWhen } from "utils/utils";
import { NewBusinessServiceModal } from "./components/new-business-service-modal";
import { UpdateBusinessServiceModal } from "./components/update-business-service-modal";
@@ -85,9 +87,18 @@ const ENTITY_FIELD = "entity";
// };
export const BusinessServices: React.FC = () => {
+ // i18
const { t } = useTranslation();
+
+ // Redux
const dispatch = useDispatch();
+ // RBAC
+ const { isAllowed: isAllowedToWrite } = useKcPermission({
+ permissionsAllowed: ["controls:write"],
+ });
+
+ // Filters
const filters = [
{
key: FilterKey.NAME,
@@ -106,15 +117,18 @@ export const BusinessServices: React.FC = () => {
new Map([])
);
+ // Create and update modal states
const [isNewModalOpen, setIsNewModalOpen] = useState(false);
const [rowToUpdate, setRowToUpdate] = useState();
+ // Delete state
const {
requestDelete: requestDeleteBusinessService,
} = useDelete({
onDelete: (t: BusinessService) => deleteBusinessService(t.id!),
});
+ // Table data
const {
businessServices,
isFetching,
@@ -155,16 +169,17 @@ export const BusinessServices: React.FC = () => {
);
}, [filtersValue, paginationQuery, sortByQuery, fetchBusinessServices]);
+ // Table's rows and columns
const columns: ICell[] = [
{ title: t("terms.name"), transforms: [sortable, cellWidth(25)] },
{ title: t("terms.description"), transforms: [cellWidth(40)] },
{ title: t("terms.owner"), transforms: [sortable] },
- {
+ ...wrapInArrayWhen(isAllowedToWrite, {
title: "",
props: {
className: "pf-u-text-align-right",
},
- },
+ }),
];
const rows: IRow[] = [];
@@ -187,14 +202,14 @@ export const BusinessServices: React.FC = () => {
),
},
- {
+ ...wrapInArrayWhen(isAllowedToWrite, {
title: (
editRow(item)}
onDelete={() => deleteRow(item)}
/>
),
- },
+ }),
],
});
});
@@ -371,18 +386,20 @@ export const BusinessServices: React.FC = () => {
}
toolbar={
-
-
-
-
-
+
+
+
+
+
+
+
}
noDataState={
{
+ // i18
const { t } = useTranslation();
+
+ // Redux
const dispatch = useDispatch();
+ // RBAC
+ const { isAllowed: isAllowedToWrite } = useKcPermission({
+ permissionsAllowed: ["controls:write"],
+ });
+
+ // Filters
const filters = [
{
key: FilterKey.NAME,
@@ -89,13 +100,16 @@ export const JobFunctions: React.FC = () => {
new Map([])
);
+ // Create and update modal states
const [isNewModalOpen, setIsNewModalOpen] = useState(false);
const [rowToUpdate, setRowToUpdate] = useState();
+ // Delete state
const { requestDelete: requestDeleteJobFunction } = useDelete({
onDelete: (t: JobFunction) => deleteJobFunction(t.id!),
});
+ // Table data
const {
jobFunctions,
isFetching,
@@ -132,20 +146,19 @@ export const JobFunctions: React.FC = () => {
);
}, [filtersValue, paginationQuery, sortByQuery, fetchJobFunctions]);
- //
-
+ // Table's rows and columns
const columns: ICell[] = [
{
title: t("terms.name"),
transforms: [sortable, cellWidth(70)],
cellFormatters: [],
},
- {
+ ...wrapInArrayWhen(isAllowedToWrite, {
title: "",
props: {
className: "pf-u-text-align-right",
},
- },
+ }),
];
const rows: IRow[] = [];
@@ -156,14 +169,14 @@ export const JobFunctions: React.FC = () => {
{
title: {item.role},
},
- {
+ ...wrapInArrayWhen(isAllowedToWrite, {
title: (
setRowToUpdate(item)}
onDelete={() => deleteRow(item)}
/>
),
- },
+ }),
],
});
});
@@ -308,18 +321,20 @@ export const JobFunctions: React.FC = () => {
}
toolbar={
-
-
-
-
-
+
+
+
+
+
+
+
}
noDataState={
{
};
export const StakeholderGroups: React.FC = () => {
+ // i18
const { t } = useTranslation();
+
+ // Redux
const dispatch = useDispatch();
+ // RBAC
+ const { isAllowed: isAllowedToWrite } = useKcPermission({
+ permissionsAllowed: ["controls:write"],
+ });
+
+ // Filters
const filters = [
{
key: FilterKey.NAME,
@@ -114,15 +125,18 @@ export const StakeholderGroups: React.FC = () => {
new Map([])
);
+ // Create and update modal states
const [isNewModalOpen, setIsNewModalOpen] = useState(false);
const [rowToUpdate, setRowToUpdate] = useState();
+ // Delete state
const {
requestDelete: requestDeleteStakeholderGroup,
} = useDelete({
onDelete: (t: StakeholderGroup) => deleteStakeholderGroup(t.id!),
});
+ // Table data
const {
stakeholderGroups,
isFetching,
@@ -171,6 +185,7 @@ export const StakeholderGroups: React.FC = () => {
);
}, [filtersValue, paginationQuery, sortByQuery, fetchStakeholderGroups]);
+ // Table's rows and columns
const columns: ICell[] = [
{
title: t("terms.name"),
@@ -182,12 +197,12 @@ export const StakeholderGroups: React.FC = () => {
title: t("terms.memberCount"),
transforms: [sortable],
},
- {
+ ...wrapInArrayWhen(isAllowedToWrite, {
title: "",
props: {
className: "pf-u-text-align-right",
},
- },
+ }),
];
const rows: IRow[] = [];
@@ -208,14 +223,14 @@ export const StakeholderGroups: React.FC = () => {
{
title: item.stakeholders ? item.stakeholders.length : 0,
},
- {
+ ...wrapInArrayWhen(isAllowedToWrite, {
title: (
editRow(item)}
onDelete={() => deleteRow(item)}
/>
),
- },
+ }),
],
});
@@ -398,18 +413,20 @@ export const StakeholderGroups: React.FC = () => {
}
toolbar={
-
-
-
-
-
+
+
+
+
+
+
+
}
noDataState={
{
};
export const Stakeholders: React.FC = () => {
+ // i18
const { t } = useTranslation();
+
+ // Redux
const dispatch = useDispatch();
+ // RBAC
+ const { isAllowed: isAllowedToWrite } = useKcPermission({
+ permissionsAllowed: ["controls:write"],
+ });
+
+ // Filters
const filters = [
{
key: FilterKey.EMAIL,
@@ -125,13 +136,16 @@ export const Stakeholders: React.FC = () => {
new Map([])
);
+ // Create and update modal states
const [isNewModalOpen, setIsNewModalOpen] = useState(false);
const [rowToUpdate, setRowToUpdate] = useState();
+ // Delete state
const { requestDelete: requestDeleteStakeholder } = useDelete({
onDelete: (t: Stakeholder) => deleteStakeholder(t.id!),
});
+ // Table data
const {
stakeholders,
isFetching,
@@ -182,6 +196,7 @@ export const Stakeholders: React.FC = () => {
);
}, [filtersValue, paginationQuery, sortByQuery, fetchStakeholders]);
+ // Table's rows and columns
const columns: ICell[] = [
{
title: t("terms.email"),
@@ -194,12 +209,12 @@ export const Stakeholders: React.FC = () => {
title: t("terms.groupCount"),
transforms: [sortable],
},
- {
+ ...wrapInArrayWhen(isAllowedToWrite, {
title: "",
props: {
className: "pf-u-text-align-right",
},
- },
+ }),
];
const rows: IRow[] = [];
@@ -227,14 +242,14 @@ export const Stakeholders: React.FC = () => {
{
title: item.stakeholderGroups ? item.stakeholderGroups.length : 0,
},
- {
+ ...wrapInArrayWhen(isAllowedToWrite, {
title: (
editRow(item)}
onDelete={() => deleteRow(item)}
/>
),
- },
+ }),
],
});
@@ -415,18 +430,20 @@ export const Stakeholders: React.FC = () => {
}
toolbar={
-
-
-
-
-
+
+
+
+
+
+
+
}
noDataState={
= ({
onEdit,
onDelete,
}) => {
+ // i18
const { t } = useTranslation();
+ // RBAC
+ const { isAllowed: isAllowedToWrite } = useKcPermission({
+ permissionsAllowed: ["controls:write"],
+ });
+
+ // Table's rows and columns
const columns: ICell[] = [
{
title: t("terms.tagName"),
@@ -102,7 +110,7 @@ export const TagTable: React.FC = ({
aria-label="tag-table"
cells={columns}
rows={rows}
- actions={actions}
+ actions={isAllowedToWrite ? actions : []}
className={styles.actionColumnPadding}
>
diff --git a/src/pages/controls/tags/tags.tsx b/src/pages/controls/tags/tags.tsx
index d1b5a847..e4e6a945 100644
--- a/src/pages/controls/tags/tags.tsx
+++ b/src/pages/controls/tags/tags.tsx
@@ -32,10 +32,16 @@ import {
NoDataEmptyState,
SearchFilter,
Color,
+ VisibilityByPermission,
} from "shared/components";
-import { useTableControls, useFetchTagTypes, useDelete } from "shared/hooks";
+import {
+ useTableControls,
+ useFetchTagTypes,
+ useDelete,
+ useKcPermission,
+} from "shared/hooks";
-import { getAxiosErrorMessage } from "utils/utils";
+import { getAxiosErrorMessage, wrapInArrayWhen } from "utils/utils";
import {
deleteTag,
deleteTagType,
@@ -93,9 +99,18 @@ const getRow = (rowData: IRowData): TagType => {
};
export const Tags: React.FC = () => {
+ // i18
const { t } = useTranslation();
+
+ // Redux
const dispatch = useDispatch();
+ // RBAC
+ const { isAllowed: isAllowedToWrite } = useKcPermission({
+ permissionsAllowed: ["controls:write"],
+ });
+
+ // Filters
const filters = [
{
key: FilterKey.TAG_TYPE,
@@ -110,12 +125,14 @@ export const Tags: React.FC = () => {
new Map([])
);
+ // Create and update modal states
const [isNewTagTypeModalOpen, setIsNewTagTypeModalOpen] = useState(false);
const [rowToUpdate, setRowToUpdate] = useState();
const [isNewTagModalOpen, setIsNewTagModalOpen] = useState(false);
const [tagToUpdate, setTagToUpdate] = useState();
+ // Delete state
const { requestDelete: requestDeleteTagType } = useDelete({
onDelete: (t: TagType) => deleteTagType(t.id!),
});
@@ -123,6 +140,7 @@ export const Tags: React.FC = () => {
onDelete: (t: Tag) => deleteTag(t.id!),
});
+ // Table data
const { tagTypes, isFetching, fetchError, fetchTagTypes } = useFetchTagTypes(
true
);
@@ -196,8 +214,7 @@ export const Tags: React.FC = () => {
);
};
- //
-
+ // Table's rows and columns
const columns: ICell[] = [
{
title: t("terms.tagType"),
@@ -213,12 +230,12 @@ export const Tags: React.FC = () => {
title: t("terms.tagCount"),
transforms: [sortable],
},
- {
+ ...wrapInArrayWhen(isAllowedToWrite, {
title: "",
props: {
className: "pf-u-text-align-right",
},
- },
+ }),
];
const rows: IRow[] = [];
@@ -240,14 +257,14 @@ export const Tags: React.FC = () => {
{
title: item.tags ? item.tags.length : 0,
},
- {
+ ...wrapInArrayWhen(isAllowedToWrite, {
title: (
setRowToUpdate(item)}
onDelete={() => deleteRow(item)}
/>
),
- },
+ }),
],
});
@@ -427,7 +444,6 @@ export const Tags: React.FC = () => {
onCollapse={collapseRow}
cells={columns}
rows={rows}
- // actions={actions}
isLoading={isFetching}
loadingVariant="skeleton"
fetchError={fetchError}
@@ -451,28 +467,30 @@ export const Tags: React.FC = () => {
}
toolbar={
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
}
noDataState={
= ({
+ permissionsAllowed,
+ children,
+}) => {
+ const { isAllowed } = useKcPermission({ permissionsAllowed });
+ return <>{isAllowed && children}>;
+};
diff --git a/src/shared/hooks/index.ts b/src/shared/hooks/index.ts
index b9ba6bec..1f8415d9 100644
--- a/src/shared/hooks/index.ts
+++ b/src/shared/hooks/index.ts
@@ -10,6 +10,7 @@ export { useFetchJobFunctions } from "./useFetchJobFunctions";
export { useFetchStakeholderGroups } from "./useFetchStakeholderGroups";
export { useFetchStakeholders } from "./useFetchStakeholders";
export { useFetchTagTypes } from "./useFetchTagTypes";
+export { useKcPermission } from "./useKcPermission";
export { useMultipleFetch } from "./useMultipleFetch";
export { useQueryString } from "./useRouteAsState";
export { useToolbarFilter } from "./useToolbarFilter";
diff --git a/src/shared/hooks/useKcPermission/index.ts b/src/shared/hooks/useKcPermission/index.ts
new file mode 100644
index 00000000..ced1aafc
--- /dev/null
+++ b/src/shared/hooks/useKcPermission/index.ts
@@ -0,0 +1 @@
+export { useKcPermission } from "./useKcPermission";
diff --git a/src/shared/hooks/useKcPermission/useKcPermission.ts b/src/shared/hooks/useKcPermission/useKcPermission.ts
new file mode 100644
index 00000000..be562355
--- /dev/null
+++ b/src/shared/hooks/useKcPermission/useKcPermission.ts
@@ -0,0 +1,27 @@
+import { useKeycloak } from "@react-keycloak/web";
+import { KcPermission, KC_API_CLIENT } from "Constants";
+
+export interface IArgs {
+ permissionsAllowed: KcPermission[];
+}
+
+export interface IState {
+ isAllowed: boolean;
+}
+
+export const useKcPermission = ({ permissionsAllowed }: IArgs): IState => {
+ const { keycloak } = useKeycloak();
+
+ const isAllowed = permissionsAllowed.some((permission) => {
+ return (
+ keycloak.hasRealmRole(permission) ||
+ keycloak.hasResourceRole(permission, KC_API_CLIENT)
+ );
+ });
+
+ return {
+ isAllowed,
+ };
+};
+
+export default useKcPermission;
diff --git a/src/utils/utils.ts b/src/utils/utils.ts
index 98b172e4..15f74768 100644
--- a/src/utils/utils.ts
+++ b/src/utils/utils.ts
@@ -61,3 +61,9 @@ export const formatDate = (value: Date, includeTime = true) => {
return value.toLocaleDateString("en", options);
};
+
+// Util functions
+
+export const wrapInArrayWhen = (when: boolean, obj: any) => {
+ return when ? [obj] : [];
+};
From 20bd850d1fcf26e4a900f9cf32ec8f6cf52e1d0d Mon Sep 17 00:00:00 2001
From: Carlos Esteban Feria Vila
<2582866+carlosthe19916@users.noreply.github.com>
Date: Mon, 1 Nov 2021 16:31:39 +0100
Subject: [PATCH 3/9] Add route validation
---
src/ProtectedRoute.tsx | 47 +++++++++++++++++++
src/Routes.tsx | 29 +++++++++---
src/layout/SidebarApp/SidebarApp.tsx | 1 -
.../application-inventory.tsx | 16 +++++--
.../manage-imports/manage-imports.tsx | 25 ++++++----
5 files changed, 96 insertions(+), 22 deletions(-)
create mode 100644 src/ProtectedRoute.tsx
diff --git a/src/ProtectedRoute.tsx b/src/ProtectedRoute.tsx
new file mode 100644
index 00000000..6a4c4f7c
--- /dev/null
+++ b/src/ProtectedRoute.tsx
@@ -0,0 +1,47 @@
+import React from "react";
+import { Route, RouteProps } from "react-router-dom";
+
+import {
+ Bullseye,
+ EmptyState,
+ EmptyStateBody,
+ EmptyStateIcon,
+ EmptyStateVariant,
+ Title,
+} from "@patternfly/react-core";
+import { WarningTriangleIcon } from "@patternfly/react-icons";
+
+import { useKcPermission } from "shared/hooks";
+
+import { KcPermission } from "Constants";
+
+export interface IProtectedRouteProps extends RouteProps {
+ permissionsAllowed: KcPermission[];
+}
+
+export const ProtectedRoute: React.FC = ({
+ permissionsAllowed,
+ ...rest
+}) => {
+ const { isAllowed } = useKcPermission({
+ permissionsAllowed,
+ });
+
+ const notAuthorizedState = (
+
+
+
+
+ 403 Forbidden
+
+ You are not allowed to access this page
+
+
+ );
+
+ if (!isAllowed) {
+ return notAuthorizedState}>;
+ }
+
+ return ;
+};
diff --git a/src/Routes.tsx b/src/Routes.tsx
index 23911f7a..0678f5a9 100644
--- a/src/Routes.tsx
+++ b/src/Routes.tsx
@@ -1,7 +1,8 @@
-import React, { lazy, Suspense } from "react";
-import { Route, Switch, Redirect } from "react-router-dom";
+import { lazy, Suspense } from "react";
+import { Switch, Redirect } from "react-router-dom";
import { AppPlaceholder } from "shared/components";
+import { ProtectedRoute, IProtectedRouteProps } from "./ProtectedRoute";
import { Paths } from "./Paths";
const ApplicationInventory = lazy(
@@ -11,21 +12,37 @@ const Reports = lazy(() => import("./pages/reports"));
const Controls = lazy(() => import("./pages/controls"));
export const AppRoutes = () => {
- const routes = [
+ const routes: IProtectedRouteProps[] = [
{
component: ApplicationInventory,
path: Paths.applicationInventory,
exact: false,
+ permissionsAllowed: ["inventory:application:read"],
+ },
+ {
+ component: Reports,
+ path: Paths.reports,
+ exact: false,
+ permissionsAllowed: ["inventory:application:read"],
+ },
+ {
+ component: Controls,
+ path: Paths.controls,
+ exact: false,
+ permissionsAllowed: ["controls:read"],
},
- { component: Reports, path: Paths.reports, exact: false },
- { component: Controls, path: Paths.controls, exact: false },
];
return (
}>
{routes.map(({ path, component, ...rest }, index) => (
-
+
))}
diff --git a/src/layout/SidebarApp/SidebarApp.tsx b/src/layout/SidebarApp/SidebarApp.tsx
index 0b5f7c44..2d0ec62c 100644
--- a/src/layout/SidebarApp/SidebarApp.tsx
+++ b/src/layout/SidebarApp/SidebarApp.tsx
@@ -5,7 +5,6 @@ import { Nav, NavItem, PageSidebar, NavList } from "@patternfly/react-core";
import { Paths } from "Paths";
import { LayoutTheme } from "../LayoutUtils";
-import { useKcPermission } from "shared/hooks";
import { VisibilityByPermission } from "shared/components";
export const SidebarApp: React.FC = () => {
diff --git a/src/pages/application-inventory/application-inventory.tsx b/src/pages/application-inventory/application-inventory.tsx
index d7d17e64..00ecbd0d 100644
--- a/src/pages/application-inventory/application-inventory.tsx
+++ b/src/pages/application-inventory/application-inventory.tsx
@@ -3,6 +3,7 @@ import { Redirect, Route, Switch, useLocation } from "react-router-dom";
import { Paths } from "Paths";
import { AppPlaceholder } from "shared/components";
+import { ProtectedRoute } from "ProtectedRoute";
const ApplicationList = lazy(() => import("./application-list"));
const ApplicationAssessment = lazy(() => import("./application-assessment"));
@@ -16,26 +17,31 @@ export const ApplicationInventory: React.FC = () => {
return (
}>
-
-
-
-
-
{
toolbar={
<>
-
-
-
+
+
+
+
+
Date: Tue, 2 Nov 2021 09:29:49 +0100
Subject: [PATCH 4/9] Restore proxy settings
---
src/setupProxy.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/setupProxy.js b/src/setupProxy.js
index e403b9ad..76f4730f 100644
--- a/src/setupProxy.js
+++ b/src/setupProxy.js
@@ -4,7 +4,7 @@ module.exports = function (app) {
app.use(
"/api/controls",
createProxyMiddleware({
- target: "http://localhost:8087",
+ target: "http://localhost:8081",
changeOrigin: true,
pathRewrite: {
"^/api/controls": "/controls",
@@ -15,7 +15,7 @@ module.exports = function (app) {
app.use(
"/api/application-inventory",
createProxyMiddleware({
- target: "http://localhost:8088",
+ target: "http://localhost:8082",
changeOrigin: true,
pathRewrite: {
"^/api/application-inventory": "/application-inventory",
@@ -26,7 +26,7 @@ module.exports = function (app) {
app.use(
"/api/pathfinder",
createProxyMiddleware({
- target: "http://localhost:8089",
+ target: "http://localhost:8083",
changeOrigin: true,
pathRewrite: {
"^/api/pathfinder": "/pathfinder",
From 872d053ae4e26a6e8d40b3b050458c546ee7a9ab Mon Sep 17 00:00:00 2001
From: Carlos Esteban Feria Vila
<2582866+carlosthe19916@users.noreply.github.com>
Date: Tue, 2 Nov 2021 10:03:24 +0100
Subject: [PATCH 5/9] Save changes
---
konveyor-realm.json | 23 ++-----------------
.../application-assessment-page-header.tsx | 12 ++++++----
.../custom-wizard-footer.tsx | 19 +++++++++------
.../application-inventory.tsx | 2 +-
4 files changed, 23 insertions(+), 33 deletions(-)
diff --git a/konveyor-realm.json b/konveyor-realm.json
index 70bd99f0..4cc13ee5 100644
--- a/konveyor-realm.json
+++ b/konveyor-realm.json
@@ -103,25 +103,6 @@
"containerId": "konveyor",
"attributes": {}
},
- {
- "id": "74c6b736-7934-44de-88c0-435c927ada01",
- "name": "user",
- "description": "User of Tackle",
- "composite": true,
- "composites": {
- "client": {
- "tackle-api": [
- "controls:read",
- "inventory:application:read",
- "inventory:application-import:read",
- "pathfinder:assessment:read"
- ]
- }
- },
- "clientRole": false,
- "containerId": "konveyor",
- "attributes": {}
- },
{
"id": "85aa1467-987b-4a71-a7e1-92dffc90323b",
"name": "uma_authorization",
@@ -593,7 +574,7 @@
"defaultRoles": [
"uma_authorization",
"offline_access",
- "user"
+ "migrator"
],
"requiredCredentials": [
"password"
@@ -688,7 +669,7 @@
],
"requiredActions": [],
"realmRoles": [
- "user"
+ "migrator"
],
"notBefore": 0,
"groups": []
diff --git a/src/pages/application-inventory/application-assessment/components/application-assessment-page/application-assessment-page-header.tsx b/src/pages/application-inventory/application-assessment/components/application-assessment-page/application-assessment-page-header.tsx
index 7cfadc66..0638a2f5 100644
--- a/src/pages/application-inventory/application-assessment/components/application-assessment-page/application-assessment-page-header.tsx
+++ b/src/pages/application-inventory/application-assessment/components/application-assessment-page/application-assessment-page-header.tsx
@@ -7,7 +7,7 @@ import { Button, ButtonVariant, Modal, Text } from "@patternfly/react-core";
import { useDispatch } from "react-redux";
import { confirmDialogActions } from "store/confirmDialog";
-import { PageHeader } from "shared/components";
+import { PageHeader, VisibilityByPermission } from "shared/components";
import { useEntityModal } from "shared/hooks";
import { ApplicationDependenciesFormContainer } from "shared/containers";
@@ -76,9 +76,13 @@ export const ApplicationAssessmentPageHeader: React.FC
{application && (
-
+
+
+
)}
>
}
diff --git a/src/pages/application-inventory/application-assessment/components/custom-wizard-footer/custom-wizard-footer.tsx b/src/pages/application-inventory/application-assessment/components/custom-wizard-footer/custom-wizard-footer.tsx
index 19d32498..c5384c65 100644
--- a/src/pages/application-inventory/application-assessment/components/custom-wizard-footer/custom-wizard-footer.tsx
+++ b/src/pages/application-inventory/application-assessment/components/custom-wizard-footer/custom-wizard-footer.tsx
@@ -6,6 +6,7 @@ import {
WizardContextConsumer,
WizardFooter,
} from "@patternfly/react-core";
+import { VisibilityByPermission } from "shared/components";
export interface CustomWizardFooterProps {
isFirstStep: boolean;
@@ -47,14 +48,18 @@ export const CustomWizardFooter: React.FC = ({
>
{t("actions.save")}
-
+
+
>
) : (
-
+ {showSaveAndReviewBtn && (
-
+ )}
>
) : (