From 5b21d9faf51fe0e8927dd8d6494235b6790787b2 Mon Sep 17 00:00:00 2001 From: Peter Stanko <peter.stanko0@gmail.com> Date: Wed, 15 Aug 2018 20:52:39 +0200 Subject: [PATCH] Permissions update - onlu sysadmin and component should be able to upload files --- app.py | 9 ++++++++- management/data/__init__.py | 6 ++++++ portal/async_celery/tasks.py | 1 - portal/config.py | 2 +- portal/logging.py | 2 +- portal/rest/errors.py | 2 +- portal/rest/login.py | 4 +--- portal/rest/submissions.py | 13 +++++-------- portal/service/auth.py | 1 + portal/service/permissions.py | 9 ++++----- portal/service/submissions.py | 12 ++++++++++-- 11 files changed, 38 insertions(+), 23 deletions(-) diff --git a/app.py b/app.py index 54245c2..42b8cc2 100644 --- a/app.py +++ b/app.py @@ -160,10 +160,17 @@ def cli_submissions_cancel(sid): @submissions_cli.command('list', help="List all submissions") -def cli_submissions_cancel_all(): +def cli_submissions_list_all(): log.info(f"[CMD] List all submissions") manager.list_all_submissions() +@submissions_cli.command('clean-all', help="Clean all submissions") +def cli_submissions_list_all(): + log.info(f"[CMD] Clean all submissions") + manager.clean_all_submissions() + + + if __name__ == '__main__': cli_run_devel() diff --git a/management/data/__init__.py b/management/data/__init__.py index e8bea64..8806fa7 100644 --- a/management/data/__init__.py +++ b/management/data/__init__.py @@ -153,4 +153,10 @@ class DataManagement(object): write_entity(submission) self.db.session.commit() + def clean_all_submissions(self): + with self.app.app_context(): + for submission in Submission.query.all(): + Submission.query.filter_by(id=submission.id).delete() + self.db.session.commit() + diff --git a/portal/async_celery/tasks.py b/portal/async_celery/tasks.py index cd6d879..4ac9720 100644 --- a/portal/async_celery/tasks.py +++ b/portal/async_celery/tasks.py @@ -32,7 +32,6 @@ def upload_results_to_storage(new_submission_id: str, path: str): return entity - @celery_app.task(name='clone-submission-files') def clone_submission_files(source_id: str, target_id: str): source = find_submission(source_id) diff --git a/portal/config.py b/portal/config.py index 231990a..9d72582 100644 --- a/portal/config.py +++ b/portal/config.py @@ -46,7 +46,7 @@ class DevelopmentConfig(Config): DEBUG = True CELERY_RESULT_BACKEND = 'redis://localhost:6379/0' BROKER_URL = 'redis://localhost:6379/0' - PORTAL_STORAGE_BASE_DIR = tempfile.mkdtemp() + PORTAL_STORAGE_BASE_DIR = f"{tempfile.gettempdir()}/kontr-portal" PORTAL_STORAGE_TEST_FILES_DIR = f"{PORTAL_STORAGE_BASE_DIR}/test-files" PORTAL_STORAGE_WORKSPACE_DIR = f"{PORTAL_STORAGE_BASE_DIR}/workspace" PORTAL_STORAGE_SUBMISSIONS_DIR = f"{PORTAL_STORAGE_BASE_DIR}/submissions" diff --git a/portal/logging.py b/portal/logging.py index d5a7f67..48911f5 100644 --- a/portal/logging.py +++ b/portal/logging.py @@ -27,7 +27,7 @@ LOGGERS = { 'tests': {'handlers': ['console'], 'level': 'DEBUG', 'propagate': True}, 'management': {'handlers': ['console'], 'level': 'INFO', 'propagate': True}, 'app': {'handlers': ['console'], 'level': 'INFO', 'propagate': True}, - 'storage': {'handlers': ['console'], 'level': 'INFO', 'propagate': True}, + 'storage': {'handlers': ['console'], 'level': 'DEBUG', 'propagate': True}, } LOGGING_CONF = { diff --git a/portal/rest/errors.py b/portal/rest/errors.py index 27e6805..e03edef 100644 --- a/portal/rest/errors.py +++ b/portal/rest/errors.py @@ -14,7 +14,7 @@ log = logging.getLogger(__name__) def load_errors(app: Flask): - log.debug("[ERR] Error handlers loaded") + log.debug("[LOAD] Custom error handlers loaded") for (ex, func) in rest_api.error_handlers.items(): app.register_error_handler(ex, func) diff --git a/portal/rest/login.py b/portal/rest/login.py index d9f1850..4baab6e 100644 --- a/portal/rest/login.py +++ b/portal/rest/login.py @@ -32,9 +32,7 @@ logout_schema = auth_namespace.model('LogoutSchema', { @jwt.user_claims_loader def add_claims_to_access_token(identity): - data = 'user' - if general.find_component(identity, throws=False): - data = 'component' + data = 'user' if User.query.filter_by(id=identity).first() else 'component' return dict(type=data) diff --git a/portal/rest/submissions.py b/portal/rest/submissions.py index 6be5b90..8586d24 100644 --- a/portal/rest/submissions.py +++ b/portal/rest/submissions.py @@ -20,7 +20,7 @@ log = logging.getLogger(__name__) def submission_access_group(client, submission, course, perm): if permissions.check_client(client, course, perm): group_intersection = [ - group for group in client.groups if submission.user in group] + group for group in client.groups if submission.user in group.users] return any( group in submission.project.groups for group in group_intersection) return False @@ -183,14 +183,11 @@ class SubmissionResultFiles(Resource): def post(self, sid: str): client = auth.find_client() submission = general.find_submission(sid) - course = submission.project.course # authorization - checks = [ - check_client(client, course, ['read_submissions_all']), - submission_access_group(client, submission, course, [ - 'read_submissions_groups']) - ] - permissions.require_any_check(client, checks=checks) + permissions.require_sysadmin(client) + permissions.require_any_check(client, checks=[permissions.check_sysadmin(client), + permissions.check_component(client)]) + task = upload_results_to_storage(submission) return {'new_task': task} diff --git a/portal/service/auth.py b/portal/service/auth.py index 81c0ea9..b167d31 100644 --- a/portal/service/auth.py +++ b/portal/service/auth.py @@ -115,6 +115,7 @@ def find_client() -> Union[User, Component]: claims = get_jwt_claims() if not (claims or claims.get('type')): raise UnauthorizedError(note="No type claim.") + if claims['type'] == 'user': client = __find_client_helper(identity, 'user', find_user) elif claims['type'] == 'component': diff --git a/portal/service/permissions.py b/portal/service/permissions.py index 1cc3a71..c2b0829 100644 --- a/portal/service/permissions.py +++ b/portal/service/permissions.py @@ -43,10 +43,9 @@ def check_client(client: Union[Component, User], Returns(bool): True if checks has passed """ - check = (check_component(component=client) or - check_user(user=client, course=course, permissions=permissions)) - - return check + if isinstance(client, User): + return check_user(user=client, course=course, permissions=permissions) + return check_component(component=client) def check_component(component: Component) -> bool: @@ -179,5 +178,5 @@ def effective_permissions_for_course(user: User, course: Course) -> dict: result[key] = result.get(key) or value log.debug( f"[PERM] Effective permissions: {user.username} in course {course.codename}: {result}" - ) + ) return result diff --git a/portal/service/submissions.py b/portal/service/submissions.py index de8913f..4a123b7 100644 --- a/portal/service/submissions.py +++ b/portal/service/submissions.py @@ -143,11 +143,19 @@ def cancel_submission(submission: Submission): def send_zip(storage_submission: entities.Submission): - return flask.send_file(storage_submission.zip_path) + path = storage_submission.zip_path + if not path.exists(): + raise errors.PortalAPIError(400, f"Requested path does not exist: {path}") + log.debug(f"[SEND] Sending zip file for submission ({storage_submission.entity_id}): {path}") + return flask.send_file(str(path), attachment_filename=path.name) def send_selected_file(storage_submission: entities.Submission, path_query: str): - return flask.send_file(storage_submission.glob(path_query)) + path = storage_submission.base_dir / Path(path_query) + if not path.exists(): + raise errors.PortalAPIError(400, f"Requested path does not exist: {path}") + log.debug(f"[SEND] Sending file for submission ({storage_submission.entity_id}): {path}") + return flask.send_file(str(path)) def send_file_or_zip(storage_entity): -- GitLab