From cc927d0dae4aa823a1714503d81d0b0a97f86ab1 Mon Sep 17 00:00:00 2001
From: Noah Paige <69586985+noah-paige@users.noreply.github.com>
Date: Tue, 25 Jun 2024 10:18:44 -0400
Subject: [PATCH] Enhance Share Health Status Verify/ReApply (#1346)
### Feature or Bugfix
- Feature
### Detail
- Enhance Share UI for re-apply and verify share health status
### Relates
- #1107
### Security
Please answer the questions below briefly where applicable, or write
`N/A`. Based on
[OWASP 10](https://owasp.org/Top10/en/).
- Does this PR introduce or modify any input fields or queries - this
includes
fetching data from storage outside the application (e.g. a database, an
S3 bucket)?
- Is the input sanitized?
- What precautions are you taking before deserializing the data you
consume?
- Is injection prevented by parametrizing queries?
- Have you ensured no `eval` or similar functions are used?
- Does this PR introduce any functionality or component that requires
authorization?
- How have you ensured it respects the existing AuthN/AuthZ mechanisms?
- Are you logging failed auth attempts?
- Are you using or adding any cryptographic features?
- Do you use a standard proven implementations?
- Are the used keys controlled by the customer? Where are they stored?
- Are you introducing any new policies/roles/users?
- Have you used the least-privilege principle? How?
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
---
.../db/share_state_machines_repositories.py | 11 +++
.../services/share_item_service.py | 9 +++
.../design/components/ShareHealthStatus.js | 72 +++++++++++++++++++
frontend/src/design/components/index.js | 1 +
.../components/DatasetListItem.js | 4 +-
.../components/EnvironmentOwnedDatasets.js | 2 +-
.../modules/Shared/Shares/ShareEditForm.js | 21 +++++-
.../components/NavigateShareViewModal.js | 60 ++++++++++++++++
.../modules/Shares/components/ShareBoxList.js | 18 +++++
.../src/modules/Shares/components/index.js | 1 +
.../src/modules/Shares/views/ShareView.js | 36 ++--------
.../modules/s3_datasets_shares/test_share.py | 11 ++-
12 files changed, 209 insertions(+), 37 deletions(-)
create mode 100644 frontend/src/design/components/ShareHealthStatus.js
create mode 100644 frontend/src/modules/Shares/components/NavigateShareViewModal.js
diff --git a/backend/dataall/modules/shares_base/db/share_state_machines_repositories.py b/backend/dataall/modules/shares_base/db/share_state_machines_repositories.py
index 2da805cef..1b9905dcf 100644
--- a/backend/dataall/modules/shares_base/db/share_state_machines_repositories.py
+++ b/backend/dataall/modules/shares_base/db/share_state_machines_repositories.py
@@ -48,6 +48,17 @@ def get_share_items_states(session, share_uri, item_uris=None):
query = query.filter(ShareObjectItem.shareItemUri.in_(item_uris))
return [item.status for item in query.distinct(ShareObjectItem.status)]
+ @staticmethod
+ def get_share_items_health_states(session, share_uri, item_uris=None):
+ query = session.query(ShareObjectItem).filter(
+ and_(
+ ShareObjectItem.shareUri == share_uri,
+ )
+ )
+ if item_uris:
+ query = query.filter(ShareObjectItem.shareItemUri.in_(item_uris))
+ return [item.healthStatus for item in query.distinct(ShareObjectItem.healthStatus)]
+
@staticmethod
def update_share_object_status(session, share_uri: str, status: str) -> ShareObject:
share = ShareObjectRepository.get_share_by_uri(session, share_uri)
diff --git a/backend/dataall/modules/shares_base/services/share_item_service.py b/backend/dataall/modules/shares_base/services/share_item_service.py
index 058820c23..5ad7c1472 100644
--- a/backend/dataall/modules/shares_base/services/share_item_service.py
+++ b/backend/dataall/modules/shares_base/services/share_item_service.py
@@ -80,6 +80,9 @@ def revoke_items_share_object(uri, revoked_uris):
share = ShareObjectRepository.get_share_by_uri(session, uri)
dataset = DatasetBaseRepository.get_dataset_by_uri(session, share.datasetUri)
revoked_items_states = ShareStatusRepository.get_share_items_states(session, uri, revoked_uris)
+ revoked_items_health_states = ShareStatusRepository.get_share_items_health_states(
+ session, uri, revoked_uris
+ )
revoked_items = [ShareObjectRepository.get_share_item_by_uri(session, uri) for uri in revoked_uris]
if not revoked_items_states:
@@ -88,6 +91,12 @@ def revoke_items_share_object(uri, revoked_uris):
message='Nothing to be revoked.',
)
+ if ShareItemHealthStatus.PendingReApply.value in revoked_items_health_states:
+ raise UnauthorizedOperation(
+ action='Revoke Items from Share Object',
+ message='Cannot revoke while reapply pending for one or more items.',
+ )
+
share_sm = ShareObjectSM(share.status)
new_share_state = share_sm.run_transition(ShareObjectActions.RevokeItems.value)
diff --git a/frontend/src/design/components/ShareHealthStatus.js b/frontend/src/design/components/ShareHealthStatus.js
new file mode 100644
index 000000000..983536f67
--- /dev/null
+++ b/frontend/src/design/components/ShareHealthStatus.js
@@ -0,0 +1,72 @@
+import VerifiedUserOutlinedIcon from '@mui/icons-material/VerifiedUserOutlined';
+import GppBadOutlinedIcon from '@mui/icons-material/GppBadOutlined';
+import PendingOutlinedIcon from '@mui/icons-material/PendingOutlined';
+import DangerousOutlinedIcon from '@mui/icons-material/DangerousOutlined';
+import * as PropTypes from 'prop-types';
+import { Typography } from '@mui/material';
+import { Label } from './Label';
+
+export const ShareHealthStatus = (props) => {
+ const { status, healthStatus, lastVerificationTime } = props;
+
+ const isShared = ['Revoke_Failed', 'Share_Succeeded'].includes(status);
+ const isHealthPending = ['PendingReApply', 'PendingVerify', null].includes(
+ healthStatus
+ );
+ const setStatus = () => {
+ if (!healthStatus) return 'Undefined';
+ return healthStatus;
+ };
+
+ const setColor = () => {
+ if (!healthStatus) return 'info';
+ if (['Healthy'].includes(healthStatus)) return 'success';
+ if (['Unhealthy'].includes(healthStatus)) return 'error';
+ if (isHealthPending) return 'warning';
+ return 'info';
+ };
+
+ const setIcon = () => {
+ if (!healthStatus) return ;
+ if (['Healthy'].includes(healthStatus))
+ return ;
+ if (['Unhealthy'].includes(healthStatus))
+ return ;
+ if (['PendingReApply', 'PendingVerify'].includes(healthStatus))
+ return ;
+ return ;
+ };
+
+ if (!isShared) {
+ return (
+
+ {'Item is not Shared'}
+
+ );
+ }
+
+ return (
+