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