예제 #1
0
    def test_list_user_pagination(self, mock_cache, mock_boto3_client,
                                  mock_creds):
        # Arrange
        UserFactory.create(username='******')
        UserFactory.create(username='******')
        SourceTableFactory(dataset=MasterDataSetFactory.create(
            user_access_type='REQUIRES_AUTHENTICATION'))

        mock_user_client = mock.Mock()
        mock_user_client.list_users.side_effect = [
            {
                "UserList": [{
                    "Arn": "Arn",
                    "Email": "*****@*****.**",
                    "Role": "AUTHOR",
                    "UserName": "******",
                }],
                "NextToken":
                "foo",
            },
            {
                "UserList": [{
                    "Arn": "Arn2",
                    "Email": "*****@*****.**",
                    "Role": "AUTHOR",
                    "UserName": "******",
                }]
            },
        ]
        mock_data_client = mock.Mock()
        mock_sts_client = mock.Mock()
        mock_boto3_client.side_effect = [
            mock_user_client,
            mock_data_client,
            mock_sts_client,
        ]
        mock_creds.return_value = [mock.Mock()]

        # Act
        sync_quicksight_permissions()

        # Assert
        assert mock_user_client.update_user.call_args_list == [
            mock.call(
                AwsAccountId=mock.ANY,
                Namespace='default',
                Role='AUTHOR',
                CustomPermissionsName='author-custom-permissions',
                UserName='******',
                Email='*****@*****.**',
            ),
            mock.call(
                AwsAccountId=mock.ANY,
                Namespace='default',
                Role='AUTHOR',
                CustomPermissionsName='author-custom-permissions',
                UserName='******',
                Email='*****@*****.**',
            ),
        ]
예제 #2
0
    def test_missing_user_handled_gracefully(self, mock_cache,
                                             mock_boto3_client, mock_creds):
        # Arrange
        user = UserFactory.create(username='******')
        user2 = UserFactory.create(username='******')
        SourceTableFactory(dataset=MasterDataSetFactory.create(
            user_access_type='REQUIRES_AUTHENTICATION'))

        mock_user_client = mock.Mock()
        mock_user_client.describe_user.side_effect = [
            botocore.exceptions.ClientError(
                {
                    "Error": {
                        "Code": "ResourceNotFoundException",
                        "Message": "User not found",
                    }
                },
                'DescribeUser',
            ),
            {
                "User": {
                    "Arn": "Arn",
                    "Email": "*****@*****.**",
                    "Role": "AUTHOR"
                }
            },
        ]
        mock_data_client = mock.Mock()
        mock_sts_client = mock.Mock()
        mock_boto3_client.side_effect = [
            mock_user_client,
            mock_data_client,
            mock_sts_client,
        ]

        # Act
        sync_quicksight_permissions(user_sso_ids_to_update=[
            str(user.profile.sso_id),
            str(user2.profile.sso_id)
        ])

        # Assert
        assert mock_user_client.describe_user.call_args_list == [
            mock.call(
                AwsAccountId=mock.ANY,
                Namespace='default',
                UserName=f'quicksight_federation/{user.profile.sso_id}',
            ),
            mock.call(
                AwsAccountId=mock.ANY,
                Namespace='default',
                UserName=f'quicksight_federation/{user2.profile.sso_id}',
            ),
        ]
        assert len(mock_data_client.create_data_source.call_args_list) == 1
        assert len(mock_data_client.update_data_source.call_args_list) == 0
예제 #3
0
    def test_quicksight_link(self, mocker):
        user = UserFactory.create()
        vis = VisualisationCatalogueItemFactory.create(
            user_access_type='REQUIRES_AUTHENTICATION'
        )
        link = VisualisationLinkFactory.create(
            visualisation_type='QUICKSIGHT',
            identifier='5d75e131-20f4-48f8-b0eb-f4ebf36434f4',
            visualisation_catalogue_item=vis,
        )

        quicksight = mocker.patch(
            'dataworkspace.apps.applications.views.get_quicksight_dashboard_name_url'
        )
        quicksight.return_value = (
            'my-dashboard',
            'https://my.dashboard.quicksight.amazonaws.com',
        )

        client = Client(**get_http_sso_data(user))
        response = client.get(link.get_absolute_url())

        assert response.status_code == 200
        assert (
            'https://my.dashboard.quicksight.amazonaws.com'
            in response.content.decode(response.charset)
        )
        assert (
            'frame-src https://eu-west-2.quicksight.aws.amazon.com'
            in response['content-security-policy']
        )
예제 #4
0
    def test_user_needs_access_via_catalogue_item(self, mocker):
        user = UserFactory.create()
        vis = VisualisationCatalogueItemFactory.create(
            user_access_type='REQUIRES_AUTHORIZATION')
        link = VisualisationLinkFactory.create(
            visualisation_type='QUICKSIGHT',
            identifier=str(uuid4()),
            visualisation_catalogue_item=vis,
        )
        quicksight = mocker.patch(
            'dataworkspace.apps.applications.views.get_quicksight_dashboard_name_url'
        )
        quicksight.return_value = (
            'my-dashboard',
            'https://my.dashboard.quicksight.amazonaws.com',
        )

        client = Client(**get_http_sso_data(user))
        response = client.get(link.get_absolute_url())
        assert response.status_code == 403

        VisualisationUserPermissionFactory.create(visualisation=vis, user=user)

        response = client.get(link.get_absolute_url())
        assert response.status_code == 200
예제 #5
0
    def test_sync_doesnt_create_role_if_user_cant_access_tools(
            self, mock_hawk_request, create_tools_access_iam_role_task):
        user = UserFactory.create(email='*****@*****.**')
        user.profile.sso_id = '00000000-0000-0000-0000-000000000000'
        user.save()

        with open(
                os.path.join(
                    os.path.dirname(__file__),
                    'test_fixture_activity_stream_sso_john_smith.json',
                ),
                'r',
        ) as file:
            user_john_smith = (200, file.read())

        with open(
                os.path.join(
                    os.path.dirname(__file__),
                    'test_fixture_activity_stream_sso_empty.json',
                ),
                'r',
        ) as file:
            empty_result = (200, file.read())

        mock_hawk_request.side_effect = [user_john_smith, empty_result]

        _do_sync_activity_stream_sso_users()

        User = get_user_model()
        all_users = User.objects.all()

        assert len(all_users) == 1
        assert not create_tools_access_iam_role_task.delay.called
예제 #6
0
    def test_sync_updates_existing_users_email(self, mock_hawk_request):
        # set the email to something different to what the activity stream
        # will return to test that it gets updated
        user = UserFactory.create(email='*****@*****.**')
        user.profile.sso_id = '00000000-0000-0000-0000-000000000000'
        user.save()

        with open(
                os.path.join(
                    os.path.dirname(__file__),
                    'test_fixture_activity_stream_sso_john_smith.json',
                ),
                'r',
        ) as file:
            user_john_smith = (200, file.read())

        with open(
                os.path.join(
                    os.path.dirname(__file__),
                    'test_fixture_activity_stream_sso_empty.json',
                ),
                'r',
        ) as file:
            empty_result = (200, file.read())

        mock_hawk_request.side_effect = [user_john_smith, empty_result]

        _do_sync_activity_stream_sso_users()

        User = get_user_model()
        all_users = User.objects.all()

        assert len(all_users) == 1
        assert str(all_users[0].email) == '*****@*****.**'
예제 #7
0
    def test_metabase_link(self, mocker):
        user = UserFactory.create()
        vis = VisualisationCatalogueItemFactory.create(
            user_access_type='REQUIRES_AUTHENTICATION'
        )
        link = VisualisationLinkFactory.create(
            visualisation_type='METABASE',
            identifier='123456789',
            visualisation_catalogue_item=vis,
        )

        jwt_encode = mocker.patch('dataworkspace.apps.applications.views.jwt.encode')
        jwt_encode.return_value = b'my-token'

        client = Client(**get_http_sso_data(user))
        response = client.get(link.get_absolute_url())

        assert response.status_code == 200
        assert (
            '//metabase.dataworkspace.test:8000/embed/dashboard/my-token#bordered=false&titled=false'
            in response.content.decode(response.charset)
        )
        assert (
            'frame-src metabase.dataworkspace.test'
            in response['content-security-policy']
        )
예제 #8
0
    def test_invalid_link_404s(self):
        user = UserFactory.create()

        client = Client(**get_http_sso_data(user))
        response = client.get(
            reverse(
                'visualisations:link',
                kwargs={"link_id": "2af5890a-bbcc-4e7d-8b2d-2a63139b3e4f"},
            ))
        assert response.status_code == 404
예제 #9
0
def test_appstream_link_only_shown_to_user_with_permission(
        has_appstream_update, expected_href, expected_text):
    user = UserFactory.create(is_staff=False, is_superuser=False)
    if has_appstream_update:
        perm = Permission.objects.get(codename='access_appstream')
        user.user_permissions.add(perm)
    user.save()
    client = Client(**get_http_sso_data(user))

    response = client.get(reverse("applications:tools"))

    soup = BeautifulSoup(response.content.decode(response.charset))
    quicksight_link = soup.find('a', href=True, text=expected_text)
    assert quicksight_link.get('href') == expected_href
예제 #10
0
    def test_datastudio_link(self):
        user = UserFactory.create()
        vis = VisualisationCatalogueItemFactory.create(
            user_access_type='REQUIRES_AUTHENTICATION')
        link = VisualisationLinkFactory.create(
            visualisation_type='DATASTUDIO',
            identifier='https://www.data.studio',
            visualisation_catalogue_item=vis,
        )

        client = Client(**get_http_sso_data(user))
        response = client.get(link.get_absolute_url())

        assert response.status_code == 302
        assert response['location'] == 'https://www.data.studio'
예제 #11
0
    def test_unauthorised_visualisation(self, has_access):
        user = UserFactory.create()
        vis = VisualisationCatalogueItemFactory.create(
            visualisation_template__user_access_type='REQUIRES_AUTHORIZATION')

        if has_access:
            ApplicationTemplateUserPermissionFactory.create(
                application_template=vis.visualisation_template, user=user)

        client = Client(**get_http_sso_data(user))
        response = client.get(vis.get_absolute_url())

        assert response.status_code == 200
        assert vis.name in response.content.decode(response.charset)
        assert ("You do not have permission to access this data visualisation."
                in response.content.decode(response.charset)) is not has_access
예제 #12
0
    def test_task_creates_iam_role(self, mock_create_tools_access_iam_role):

        user = UserFactory.create(username='******')
        user.profile.sso_id = '00000000-0000-0000-0000-000000000001'
        user.profile.home_directory_efs_access_point_id = 'some-access-point-id'
        user.save()

        _do_create_tools_access_iam_role(user.id)

        assert mock_create_tools_access_iam_role.call_args_list == [
            mock.call(
                '*****@*****.**',
                '00000000-0000-0000-0000-000000000001',
                'some-access-point-id',
            )
        ]
예제 #13
0
    def test_task_creates_iam_role(self, mock_create_tools_access_iam_role):

        user = UserFactory.create(username="******")
        user.profile.sso_id = "00000000-0000-0000-0000-000000000001"
        user.profile.home_directory_efs_access_point_id = "some-access-point-id"
        user.save()

        _do_create_tools_access_iam_role(user.id)

        assert mock_create_tools_access_iam_role.call_args_list == [
            mock.call(
                "*****@*****.**",
                "00000000-0000-0000-0000-000000000001",
                "some-access-point-id",
            )
        ]
예제 #14
0
    def test_user_needs_access_via_catalogue_item(self):
        user = UserFactory.create()
        vis = VisualisationCatalogueItemFactory.create(
            user_access_type='REQUIRES_AUTHORIZATION')
        link = VisualisationLinkFactory.create(
            visualisation_type='METABASE',
            identifier='123',
            visualisation_catalogue_item=vis,
        )

        client = Client(**get_http_sso_data(user))
        response = client.get(link.get_absolute_url())
        assert response.status_code == 403

        VisualisationUserPermissionFactory.create(visualisation=vis, user=user)

        response = client.get(link.get_absolute_url())
        assert response.status_code == 200
예제 #15
0
    def test_sync_creates_role_if_user_can_access_tools(
        self, mock_hawk_request, create_tools_access_iam_role_task
    ):
        can_access_tools_permission = Permission.objects.get(
            codename="start_all_applications",
            content_type=ContentType.objects.get_for_model(ApplicationInstance),
        )
        user = UserFactory.create(email="*****@*****.**")
        user.profile.sso_id = "00000000-0000-0000-0000-000000000000"
        user.save()
        user.user_permissions.add(can_access_tools_permission)

        with open(
            os.path.join(
                os.path.dirname(__file__),
                "test_fixture_activity_stream_sso_john_smith.json",
            ),
            "r",
        ) as file:
            user_john_smith = (200, file.read())

        with open(
            os.path.join(
                os.path.dirname(__file__),
                "test_fixture_activity_stream_sso_empty.json",
            ),
            "r",
        ) as file:
            empty_result = (200, file.read())

        mock_hawk_request.side_effect = [user_john_smith, empty_result]

        _do_sync_activity_stream_sso_users()

        User = get_user_model()
        all_users = User.objects.all()

        assert len(all_users) == 1
        assert create_tools_access_iam_role_task.delay.call_args_list == [
            mock.call(
                user.id,
            )
        ]
예제 #16
0
    def test_sync_doesnt_create_role_if_user_already_has_role(
            self, mock_hawk_request, create_tools_access_iam_role_task):
        can_access_tools_permission = Permission.objects.get(
            codename='start_all_applications',
            content_type=ContentType.objects.get_for_model(
                ApplicationInstance),
        )
        user = UserFactory.create(email='*****@*****.**')
        user.user_permissions.add(can_access_tools_permission)
        user.profile.sso_id = '00000000-0000-0000-0000-000000000000'
        user.profile.tools_access_role_arn = 'some-arn'
        user.save()

        with open(
                os.path.join(
                    os.path.dirname(__file__),
                    'test_fixture_activity_stream_sso_john_smith.json',
                ),
                'r',
        ) as file:
            user_john_smith = (200, file.read())

        with open(
                os.path.join(
                    os.path.dirname(__file__),
                    'test_fixture_activity_stream_sso_empty.json',
                ),
                'r',
        ) as file:
            empty_result = (200, file.read())

        mock_hawk_request.side_effect = [user_john_smith, empty_result]

        _do_sync_activity_stream_sso_users()

        User = get_user_model()
        all_users = User.objects.all()

        assert len(all_users) == 1
        assert not create_tools_access_iam_role_task.delay.called
예제 #17
0
    def test_sync_updates_existing_users_sso_id_and_email(
            self, mock_hawk_request):
        # set the sso id to something different to what the activity stream
        # will return and set the email to the third email in the list that
        # the activity stream will return to test that it is able to look up
        # the user and update both their email and sso id
        user = UserFactory.create(email='*****@*****.**')
        user.profile.sso_id = '00000000-0000-0000-0000-111111111111'
        user.save()

        with open(
                os.path.join(
                    os.path.dirname(__file__),
                    'test_fixture_activity_stream_sso_john_smith_multiple_emails.json',
                ),
                'r',
        ) as file:
            user_john_smith = (200, file.read())

        with open(
                os.path.join(
                    os.path.dirname(__file__),
                    'test_fixture_activity_stream_sso_empty.json',
                ),
                'r',
        ) as file:
            empty_result = (200, file.read())

        mock_hawk_request.side_effect = [user_john_smith, empty_result]

        _do_sync_activity_stream_sso_users()

        User = get_user_model()
        all_users = User.objects.all()

        assert len(all_users) == 1
        assert (str(all_users[0].profile.sso_id) ==
                '00000000-0000-0000-0000-000000000000')
        assert str(all_users[0].email) == '*****@*****.**'
예제 #18
0
    def test_create_new_data_source(self, mock_cache, mock_boto3_client,
                                    mock_creds):
        # Arrange
        UserFactory.create(username='******')
        SourceTableFactory(dataset=MasterDataSetFactory.create(
            user_access_type='REQUIRES_AUTHENTICATION'))

        mock_user_client = mock.Mock()
        mock_user_client.list_users.return_value = {
            "UserList": [{
                "Arn": "Arn",
                "Email": "*****@*****.**",
                "Role": "AUTHOR",
                "UserName": "******",
            }]
        }
        mock_data_client = mock.Mock()
        mock_sts_client = mock.Mock()
        mock_boto3_client.side_effect = [
            mock_user_client,
            mock_data_client,
            mock_sts_client,
        ]
        mock_creds.return_value = [mock.Mock()]

        # Act
        sync_quicksight_permissions()

        # Assert
        assert mock_user_client.update_user.call_args_list == [
            mock.call(
                AwsAccountId=mock.ANY,
                Namespace='default',
                Role='AUTHOR',
                CustomPermissionsName='author-custom-permissions',
                UserName='******',
                Email='*****@*****.**',
            )
        ]
        assert mock_data_client.create_data_source.call_args_list == [
            mock.call(
                AwsAccountId=mock.ANY,
                DataSourceId=mock.ANY,
                Name=mock.ANY,
                DataSourceParameters={
                    'AuroraPostgreSqlParameters': {
                        'Host': mock.ANY,
                        'Port': mock.ANY,
                        'Database': mock.ANY,
                    }
                },
                Credentials={
                    'CredentialPair': {
                        'Username': mock.ANY,
                        'Password': mock.ANY
                    }
                },
                VpcConnectionProperties={'VpcConnectionArn': mock.ANY},
                Type='AURORA_POSTGRESQL',
                Permissions=[{
                    'Principal':
                    'Arn',
                    'Actions': [
                        'quicksight:DescribeDataSource',
                        'quicksight:DescribeDataSourcePermissions',
                        'quicksight:PassDataSource',
                    ],
                }],
            )
        ]
        assert mock_data_client.update_data_source.call_args_list == []
        assert sorted(
            mock_data_client.delete_data_source.call_args_list,
            key=lambda x: x.kwargs['DataSourceId'],
        ) == [
            mock.call(
                AwsAccountId=mock.ANY,
                DataSourceId='data-workspace-dev-my_database-88f3887d',
            ),
            mock.call(
                AwsAccountId=mock.ANY,
                DataSourceId='data-workspace-dev-test_external_db2-88f3887d',
            ),
        ]
예제 #19
0
    def test_poll_until_user_created(self, mock_cache, mock_boto3_client,
                                     mock_creds):
        # Arrange
        user = UserFactory.create(username='******')
        SourceTableFactory(dataset=MasterDataSetFactory.create(
            user_access_type='REQUIRES_AUTHENTICATION'))

        mock_user_client = mock.Mock()
        mock_user_client.describe_user.side_effect = [
            botocore.exceptions.ClientError(
                {
                    "Error": {
                        "Code": "ResourceNotFoundException",
                        "Message": "User not found",
                    }
                },
                'DescribeUser',
            ),
        ] * 10 + [{
            "User": {
                "Arn": "Arn",
                "Email": "*****@*****.**",
                "Role": "AUTHOR",
                "UserName": "******",
            }
        }]
        mock_data_client = mock.Mock()
        mock_sts_client = mock.Mock()
        mock_boto3_client.side_effect = [
            mock_user_client,
            mock_data_client,
            mock_sts_client,
        ]

        # Act
        with mock.patch('dataworkspace.apps.applications.utils.gevent.sleep'):
            sync_quicksight_permissions(
                user_sso_ids_to_update=[str(user.profile.sso_id)],
                poll_for_user_creation=True,
            )

        # Assert
        assert mock_user_client.update_user.call_args_list == [
            mock.call(
                AwsAccountId=mock.ANY,
                Namespace='default',
                Role='AUTHOR',
                CustomPermissionsName='author-custom-permissions',
                UserName='******',
                Email='*****@*****.**',
            )
        ]
        assert (mock_user_client.describe_user.call_args_list == [
            mock.call(
                AwsAccountId=mock.ANY,
                Namespace='default',
                UserName=f'quicksight_federation/{user.profile.sso_id}',
            ),
        ] * 11)
        assert len(mock_data_client.create_data_source.call_args_list) == 1
        assert len(mock_data_client.update_data_source.call_args_list) == 0
예제 #20
0
    def test_create_new_data_source(self, mock_cache, mock_boto3_client, mock_creds):
        # Arrange
        UserFactory.create(username="******")
        SourceTableFactory(
            dataset=MasterDataSetFactory.create(
                user_access_type=UserAccessType.REQUIRES_AUTHENTICATION
            )
        )

        mock_user_client = mock.Mock()
        mock_user_client.list_users.return_value = {
            "UserList": [
                {
                    "Arn": "Arn",
                    "Email": "*****@*****.**",
                    "Role": "AUTHOR",
                    "UserName": "******",
                }
            ]
        }
        mock_data_client = mock.Mock()
        mock_sts_client = mock.Mock()
        mock_boto3_client.side_effect = [
            mock_user_client,
            mock_data_client,
            mock_sts_client,
        ]
        mock_creds.return_value = [mock.Mock()]

        # Act
        sync_quicksight_permissions()

        # Assert
        assert mock_user_client.update_user.call_args_list == [
            mock.call(
                AwsAccountId=mock.ANY,
                Namespace="default",
                Role="AUTHOR",
                CustomPermissionsName="author-custom-permissions",
                UserName="******",
                Email="*****@*****.**",
            )
        ]
        assert mock_data_client.create_data_source.call_args_list == [
            mock.call(
                AwsAccountId=mock.ANY,
                DataSourceId=mock.ANY,
                Name=mock.ANY,
                DataSourceParameters={
                    "AuroraPostgreSqlParameters": {
                        "Host": mock.ANY,
                        "Port": mock.ANY,
                        "Database": mock.ANY,
                    }
                },
                Credentials={"CredentialPair": {"Username": mock.ANY, "Password": mock.ANY}},
                VpcConnectionProperties={"VpcConnectionArn": mock.ANY},
                Type="AURORA_POSTGRESQL",
                Permissions=[
                    {
                        "Principal": "Arn",
                        "Actions": [
                            "quicksight:DescribeDataSource",
                            "quicksight:DescribeDataSourcePermissions",
                            "quicksight:PassDataSource",
                        ],
                    }
                ],
            )
        ]
        assert mock_data_client.update_data_source.call_args_list == []
        assert sorted(
            mock_data_client.delete_data_source.call_args_list,
            key=lambda x: x.kwargs["DataSourceId"],
        ) == [
            mock.call(
                AwsAccountId=mock.ANY,
                DataSourceId="data-workspace-dev-my_database-88f3887d",
            ),
            mock.call(
                AwsAccountId=mock.ANY,
                DataSourceId="data-workspace-dev-test_external_db2-88f3887d",
            ),
        ]