Skip to content

Commit

Permalink
Dashboard Integration Test Improvements (#1623)
Browse files Browse the repository at this point in the history
### Feature or Bugfix
<!-- please choose -->
- Test Enhancement

### Detail
- Add documentation in README on how to set up dashboard tests
- Add check for QS Account and skip if no Account exists in
`session_env1`

### Relates

### 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.
  • Loading branch information
noah-paige authored Oct 10, 2024
1 parent abbb10a commit 1a0c4b5
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 16 deletions.
10 changes: 10 additions & 0 deletions backend/dataall/core/environment/cdk/environment_stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -678,3 +678,13 @@ def create_integration_tests_role(self):
resources=[f'arn:aws:iam::{self.account}:role/dataall-test-*'],
),
)

self.test_role.add_to_policy(
iam.PolicyStatement(
actions=[
'quicksight:DescribeAccountSubscription',
],
effect=iam.Effect.ALLOW,
resources=[f'arn:aws:quicksight:*:{self.account}:*'],
),
)
12 changes: 12 additions & 0 deletions tests_new/integration_tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,18 @@ Currently **we support only Cognito based deployments** but support for any IdP

- The pipeline will create the users/groups

### Dashboard Tests Pre-Requisities

In order to run the tests on the dashboards module the following steps are required:

- Create Enterprise QuickSight Subscription in `session_env1` AWS Account
- Update QuickSight Account with a Reader Capacity Pricing Plan (required for generating embed URLs - `GenerateEmbedUrlForAnonymousUser`)
- Create / Publish a QuickSight Dashboard
- Create a QuickSight Group named `dataall` and give owner access of the published dashboard to the `dataall` group
- Provide the `dashboardId` in the `config.json` as shown above

Rather than failing if the above pre-requisites are not completed, if ther eis no QuickSight Account is detected in `session_env1` the dashboard tests will be **skipped**.

## Run tests

The tests are executed in CodeBuild as part of the CICD pipeline if the cdk.json parameter `with_approval_tests` is set
Expand Down
37 changes: 37 additions & 0 deletions tests_new/integration_tests/modules/dashboards/aws_clients.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import logging

log = logging.getLogger(__name__)


class QuickSightClient:
def __init__(self, session, account_id, region):
self._client = session.client('quicksight', region_name=region)
self._region = region
self._account_id = account_id

def check_enterprise_account_exists(self):
"""
Check if a QuickSight Account exists in the account.
:param
:return: True if the account exists, False otherwise
"""
try:
response = self._client.describe_account_subscription(AwsAccountId=self._account_id)
if not response['AccountInfo']:
log.info(f'Quicksight Enterprise Subscription not found in Account: {self._account_id}')
return False
else:
if response['AccountInfo']['Edition'] not in ['ENTERPRISE', 'ENTERPRISE_AND_Q']:
log.info(
f"Quicksight Subscription found in Account: {self._account_id} of incorrect type: {response['AccountInfo']['Edition']}"
)
return False
else:
if response['AccountInfo']['AccountSubscriptionStatus'] == 'ACCOUNT_CREATED':
return True
log.info(
f"Quicksight Subscription found in Account: {self._account_id} not active. Status = {response['AccountInfo']['AccountSubscriptionStatus']}"
)
return False
except self._client.exceptions.ResourceNotFoundException:
return False
9 changes: 9 additions & 0 deletions tests_new/integration_tests/modules/dashboards/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@
)
from integration_tests.modules.dashboards.queries import get_dashboard
from integration_tests.core.environment.utils import set_env_params
from integration_tests.modules.dashboards.aws_clients import QuickSightClient


@pytest.fixture(scope='session')
def quicksight_account_exists(session_env1, session_env1_aws_client):
if not QuickSightClient(
session_env1_aws_client, session_env1.AwsAccountId, session_env1.region
).check_enterprise_account_exists():
pytest.skip('Skipping QuickSight tests because QuickSight account does not exist')


def create_dataall_dashboard(client, session_id, dashboard_id, env):
Expand Down
32 changes: 16 additions & 16 deletions tests_new/integration_tests/modules/dashboards/test_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,61 +20,61 @@
UPDATED_DESC = 'new description'


def test_get_author_session(client1, session_env1):
def test_get_author_session(quicksight_account_exists, client1, session_env1):
set_env_params(client1, session_env1, dashboardsEnabled='true')
assert_that(get_author_session(client1, session_env1.environmentUri)).starts_with('https://')


def test_get_author_session_unauthorized(client2, session_env1):
def test_get_author_session_unauthorized(quicksight_account_exists, client2, session_env1):
assert_that(get_author_session).raises(GqlError).when_called_with(client2, session_env1.environmentUri).contains(
'UnauthorizedOperation', 'CREATE_DASHBOARD', session_env1.environmentUri
)


def test_get_dashboard(session_id, dashboard1):
def test_get_dashboard(quicksight_account_exists, session_id, dashboard1):
assert_that(dashboard1.label).is_equal_to(session_id)


def test_list_dashboards(client1, client2, session_id, dashboard1):
def test_list_dashboards(quicksight_account_exists, client1, client2, session_id, dashboard1):
filter = {'term': session_id}
assert_that(search_dashboards(client1, filter).nodes).is_length(1)
assert_that(search_dashboards(client2, filter).nodes).is_length(0)


def test_get_dashboard_unauthorized(client2, dashboard1):
def test_get_dashboard_unauthorized(quicksight_account_exists, client2, dashboard1):
assert_that(get_dashboard).raises(GqlError).when_called_with(client2, dashboard1.dashboardUri).contains(
'UnauthorizedOperation', 'GET_DASHBOARD', dashboard1.dashboardUri
)


def test_update_dashboard(client1, dashboard1):
def test_update_dashboard(quicksight_account_exists, client1, dashboard1):
update_dashboard(client1, {'dashboardUri': dashboard1.dashboardUri, 'description': UPDATED_DESC})
ds = get_dashboard(client1, dashboard1.dashboardUri)
assert_that(ds.description).is_equal_to(UPDATED_DESC)


def test_update_dashboard_unauthorized(client2, dashboard1):
def test_update_dashboard_unauthorized(quicksight_account_exists, client2, dashboard1):
assert_that(update_dashboard).raises(GqlError).when_called_with(
client2, {'dashboardUri': dashboard1.dashboardUri, 'description': UPDATED_DESC}
).contains('UnauthorizedOperation', 'UPDATE_DASHBOARD', dashboard1.dashboardUri)


def test_request_dashboard_share(dashboard1_share):
def test_request_dashboard_share(quicksight_account_exists, dashboard1_share):
assert_that(dashboard1_share.shareUri).is_not_none()
assert_that(dashboard1_share.status).is_equal_to('REQUESTED')


def test_list_dashboard_shares(client1, session_id, dashboard1, dashboard1_share):
def test_list_dashboard_shares(quicksight_account_exists, client1, session_id, dashboard1, dashboard1_share):
assert_that(list_dashboard_shares(client1, dashboard1.dashboardUri, {'term': session_id}).nodes).is_length(1)


def test_approve_dashboard_share_unauthorized(client2, dashboard1, dashboard1_share):
def test_approve_dashboard_share_unauthorized(quicksight_account_exists, client2, dashboard1, dashboard1_share):
assert_that(approve_dashboard_share).raises(GqlError).when_called_with(client2, dashboard1_share.shareUri).contains(
'UnauthorizedOperation', 'SHARE_DASHBOARD', dashboard1.dashboardUri
)


def test_approve_dashboard_share(client1, client2, session_id, dashboard1, dashboard1_share):
def test_approve_dashboard_share(quicksight_account_exists, client1, client2, session_id, dashboard1, dashboard1_share):
filter = {'term': session_id}
assert_that(search_dashboards(client2, filter).nodes).is_length(0)
ds_share = approve_dashboard_share(client1, dashboard1_share.shareUri)
Expand All @@ -83,23 +83,23 @@ def test_approve_dashboard_share(client1, client2, session_id, dashboard1, dashb
assert_that(search_dashboards(client2, filter).nodes).is_length(1)


def test_reject_dashboard_share(client1, client2, session_id, dashboard1_share):
def test_reject_dashboard_share(quicksight_account_exists, client1, client2, session_id, dashboard1_share):
ds_share = reject_dashboard_share(client1, dashboard1_share.shareUri)
assert_that(ds_share.status).is_equal_to('REJECTED')
assert_that(search_dashboards(client2, {'term': session_id}).nodes).is_length(0)


def test_get_reader_session(client1, dashboard1):
def test_get_reader_session(quicksight_account_exists, client1, dashboard1):
assert_that(get_reader_session(client1, dashboard1.dashboardUri)).starts_with('https://')


def test_get_reader_session_unauthorized(client2, dashboard1):
def test_get_reader_session_unauthorized(quicksight_account_exists, client2, dashboard1):
assert_that(get_reader_session).raises(GqlError).when_called_with(client2, dashboard1.dashboardUri).contains(
'UnauthorizedOperation', 'GET_DASHBOARD', dashboard1.dashboardUri
)


def test_delete_dashboard(client1, session_id, session_env1, testdata):
def test_delete_dashboard(quicksight_account_exists, client1, session_id, session_env1, testdata):
filter = {'term': session_id}
dashboardId = testdata.dashboards['session_env1'].dashboardId
dashboard2 = create_dataall_dashboard(client1, session_id, dashboardId, session_env1)
Expand All @@ -109,7 +109,7 @@ def test_delete_dashboard(client1, session_id, session_env1, testdata):
assert_that(search_dashboards(client1, filter).nodes).is_length(1)


def test_delete_dashboard_unauthorized(client2, dashboard1):
def test_delete_dashboard_unauthorized(quicksight_account_exists, client2, dashboard1):
assert_that(delete_dashboard).raises(GqlError).when_called_with(client2, dashboard1.dashboardUri).contains(
'UnauthorizedOperation', 'DELETE_DASHBOARD', dashboard1.dashboardUri
)

0 comments on commit 1a0c4b5

Please sign in to comment.