From a800aae375d8e57f0bfa86d3cf55023385726c34 Mon Sep 17 00:00:00 2001 From: Sal Tijerina Date: Wed, 15 Nov 2023 15:37:09 -0600 Subject: [PATCH] fix show path; fix public path; prevent access outside public path --- .../DataFilesTable/DataFilesTable.jsx | 13 ++++++++++++- .../src/components/PublicData/PublicData.jsx | 6 +++++- server/portal/apps/datafiles/views.py | 19 ++++++++++++++----- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/client/src/components/DataFiles/DataFilesTable/DataFilesTable.jsx b/client/src/components/DataFiles/DataFilesTable/DataFilesTable.jsx index 4d28b7bf2..189fc83f1 100644 --- a/client/src/components/DataFiles/DataFilesTable/DataFilesTable.jsx +++ b/client/src/components/DataFiles/DataFilesTable/DataFilesTable.jsx @@ -10,7 +10,7 @@ import { useSelector, useDispatch } from 'react-redux'; import { useTable, useBlockLayout } from 'react-table'; import { FixedSizeList, areEqual } from 'react-window'; import AutoSizer from 'react-virtualized-auto-sizer'; -import { Link } from 'react-router-dom'; +import { Link, useLocation } from 'react-router-dom'; import { useFileListing, useSystems } from 'hooks/datafiles'; import { LoadingSpinner, SectionMessage } from '_common'; import './DataFilesTable.scss'; @@ -19,6 +19,9 @@ import * as ROUTES from '../../../constants/routes'; // What to render if there are no files to display const DataFilesTablePlaceholder = ({ section, data }) => { const { params, error: err, loading } = useFileListing(section); + + const isPublicSystem = params.scheme === 'public'; + const { api: currentListing, scheme } = params ?? {}; const dispatch = useDispatch(); @@ -170,6 +173,14 @@ const DataFilesTablePlaceholder = ({ section, data }) => { ); } if (err === '403') { + if (isPublicSystem) + return ( +
+ + You must be logged in to view this data. + +
+ ); return (
diff --git a/client/src/components/PublicData/PublicData.jsx b/client/src/components/PublicData/PublicData.jsx index a4e8dd22c..40724764f 100644 --- a/client/src/components/PublicData/PublicData.jsx +++ b/client/src/components/PublicData/PublicData.jsx @@ -14,6 +14,7 @@ import { useSelectedFiles } from 'hooks/datafiles'; import DataFilesBreadcrumbs from '../DataFiles/DataFilesBreadcrumbs/DataFilesBreadcrumbs'; import DataFilesListing from '../DataFiles/DataFilesListing/DataFilesListing'; import DataFilesPreviewModal from '../DataFiles/DataFilesModals/DataFilesPreviewModal'; +import DataFilesShowPathModal from '../DataFiles/DataFilesModals/DataFilesShowPathModal'; import { ToolbarButton } from '../DataFiles/DataFilesToolbar/DataFilesToolbar'; import styles from './PublicData.module.css'; @@ -46,7 +47,9 @@ const PublicData = () => { useEffect(() => { const pathLength = location.pathname.split('/').length; if (publicDataSystem.system && pathLength < 6) { - history.push(`/public-data/tapis/public/${publicDataSystem.system}/`); + history.push( + `/public-data/tapis/public/${publicDataSystem.system}${publicDataSystem.homeDir}` + ); } }, [publicDataSystem.system]); @@ -67,6 +70,7 @@ const PublicData = () => { + ); }; diff --git a/server/portal/apps/datafiles/views.py b/server/portal/apps/datafiles/views.py index 8f0e769cd..93690a026 100644 --- a/server/portal/apps/datafiles/views.py +++ b/server/portal/apps/datafiles/views.py @@ -2,7 +2,8 @@ import logging from portal.apps.users.utils import get_allocations from django.conf import settings -from django.http import JsonResponse, HttpResponseForbidden +from django.http import JsonResponse, HttpResponseForbidden, HttpResponseRedirect +from django.urls import reverse from requests.exceptions import HTTPError from tapipy.errors import InternalServerError from portal.views.base import BaseApiView @@ -60,12 +61,20 @@ def get(self, request): return JsonResponse(response) -@method_decorator(login_required, name='dispatch') class SystemDefinitionView(BaseApiView): """Get definitions for individual systems""" def get(self, request, systemId): - system_def = request.user.tapis_oauth.client.systems.getSystem(systemId=systemId) + try: + client = request.user.tapis_oauth.client + except AttributeError: + # Make sure that we only let unauth'd users see public systems + public_sys = next((sys for sys in settings.PORTAL_DATAFILES_STORAGE_SYSTEMS if sys['scheme'] == 'public'), None) + if public_sys and public_sys['system'] == systemId: + client = service_account() + else: + return JsonResponse({'message': 'Unauthorized'}, status=401) + system_def = client.systems.getSystem(systemId=systemId) return JsonResponse( { "status": 200, @@ -81,8 +90,8 @@ def get(self, request, operation=None, scheme=None, system=None, path='/'): client = request.user.tapis_oauth.client except AttributeError: # Make sure that we only let unauth'd users see public systems - if next((sys for sys in settings.PORTAL_DATAFILES_STORAGE_SYSTEMS - if sys['system'] == system and sys['scheme'] == 'public'), None): + public_sys = next((sys for sys in settings.PORTAL_DATAFILES_STORAGE_SYSTEMS if sys['scheme'] == 'public'), None) + if public_sys and public_sys['system'] == system and path.startswith(public_sys['homeDir'].strip('/')): client = service_account() else: return JsonResponse(