Verified Commit 56534c03 authored by Peter Stanko's avatar Peter Stanko
Browse files

Access logging and accountability support

parent 47aa4200
Pipeline #16575 passed with stage
in 8 minutes and 41 seconds
...@@ -8,7 +8,7 @@ from typing import Union ...@@ -8,7 +8,7 @@ from typing import Union
from celery import Celery from celery import Celery
from flask import Flask from flask import Flask
from flask_cors import CORS from flask_cors import CORS
from flask_jwt_extended import JWTManager from flask_jwt_extended import JWTManager, get_jwt_identity
from flask_migrate import Migrate from flask_migrate import Migrate
from flask_oauthlib.client import OAuth from flask_oauthlib.client import OAuth
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
...@@ -127,7 +127,6 @@ def create_app(environment: str = None): ...@@ -127,7 +127,6 @@ def create_app(environment: str = None):
from flask import request from flask import request
body = request.get_data(as_text=True) if request.data else '' body = request.get_data(as_text=True) if request.data else ''
app.logger.debug(f"[{request.method}] {request.url} - {body}") app.logger.debug(f"[{request.method}] {request.url} - {body}")
return app return app
......
...@@ -7,6 +7,18 @@ from logging.config import dictConfig ...@@ -7,6 +7,18 @@ from logging.config import dictConfig
from portal.tools import paths from portal.tools import paths
def get_logger_file(name):
return {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'verbose',
'filename': str(paths.LOG_DIR / f'{name}.log'),
'maxBytes': 5000000, # 5MB
'backupCount': 5
}
FORMATTERS = { FORMATTERS = {
'verbose': { 'verbose': {
'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s' 'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
...@@ -27,24 +39,23 @@ HANDLERS = { ...@@ -27,24 +39,23 @@ HANDLERS = {
'class': 'logging.StreamHandler', 'class': 'logging.StreamHandler',
'formatter': 'colored_console' 'formatter': 'colored_console'
}, },
'file': { 'portal_file': get_logger_file('portal'),
'level': 'DEBUG', 'access_file': get_logger_file('access'),
'class': 'logging.handlers.RotatingFileHandler', 'storage_file': get_logger_file('storage'),
'formatter': 'verbose', 'flask_file': get_logger_file('flask')
'filename': str(paths.LOG_DIR / 'portal.log'),
'maxBytes': 500000,
'backupCount': 5
}
} }
LOGGERS = { LOGGERS = {
'portal': {'handlers': ['console', 'file'], 'level': 'DEBUG', 'propagate': True}, 'portal': {'handlers': ['console', 'portal_file'], 'level': 'DEBUG', 'propagate': True},
'portal.access_log': {
'handlers': ['console', 'access_file'], 'level': 'DEBUG', 'propagate': True
},
'tests': {'handlers': ['console'], 'level': 'DEBUG', 'propagate': True}, 'tests': {'handlers': ['console'], 'level': 'DEBUG', 'propagate': True},
'management': {'handlers': ['console'], 'level': 'INFO', 'propagate': True}, 'management': {'handlers': ['console'], 'level': 'INFO', 'propagate': True},
'app': {'handlers': ['console'], 'level': 'DEBUG', 'propagate': True}, 'app': {'handlers': ['console'], 'level': 'DEBUG', 'propagate': True},
'flask': {'handlers': ['console'], 'level': 'DEBUG', 'propagate': True}, 'flask': {'handlers': ['console', 'flask_file'], 'level': 'DEBUG', 'propagate': True},
'werkzeug': {'handlers': ['console'], 'level': 'DEBUG', 'propagate': True}, 'werkzeug': {'handlers': ['console'], 'level': 'DEBUG', 'propagate': True},
'storage': {'handlers': ['console'], 'level': 'INFO', 'propagate': True}, 'storage': {'handlers': ['console', 'storage_file'], 'level': 'INFO', 'propagate': True},
} }
LOGGING_CONF = { LOGGING_CONF = {
...@@ -84,3 +95,10 @@ def load_config(conf_type=None): ...@@ -84,3 +95,10 @@ def load_config(conf_type=None):
def get_logger(*args, **kwargs): def get_logger(*args, **kwargs):
logger = logging.getLogger(*args, **kwargs) logger = logging.getLogger(*args, **kwargs)
return logger return logger
def get_access_logger(*args, **kwargs):
return logging.getLogger('portal.access_log', *args, **kwargs)
ACCESS = get_access_logger()
...@@ -6,6 +6,7 @@ from portal.database.models import ClientType ...@@ -6,6 +6,7 @@ from portal.database.models import ClientType
from portal.rest import rest_helpers from portal.rest import rest_helpers
from portal.rest.custom_resource import CustomResource from portal.rest.custom_resource import CustomResource
from portal.rest.schemas import SCHEMAS from portal.rest.schemas import SCHEMAS
from portal.tools.decorators import access_log
client_namespace = Namespace('client') client_namespace = Namespace('client')
clients_namespace = Namespace('clients') clients_namespace = Namespace('clients')
...@@ -49,6 +50,7 @@ class ClientSecretsController(CustomResource): ...@@ -49,6 +50,7 @@ class ClientSecretsController(CustomResource):
return SCHEMAS.dump('secrets', client.secrets) return SCHEMAS.dump('secrets', client.secrets)
@jwt_required @jwt_required
@access_log
# @workers_namespace.response(201, 'Created worker secret', model=secret_schema) # @workers_namespace.response(201, 'Created worker secret', model=secret_schema)
def post(self, cid: str): def post(self, cid: str):
self.permissions.require.sysadmin_or_self(cid) self.permissions.require.sysadmin_or_self(cid)
...@@ -72,6 +74,7 @@ class ClientSecretController(CustomResource): ...@@ -72,6 +74,7 @@ class ClientSecretController(CustomResource):
return SCHEMAS.dump('secret', secret) return SCHEMAS.dump('secret', secret)
@jwt_required @jwt_required
@access_log
@clients_namespace.response(204, 'Client secret deleted') @clients_namespace.response(204, 'Client secret deleted')
def delete(self, cid: str, sid: str): def delete(self, cid: str, sid: str):
self.rest.permissions.require.sysadmin_or_self(cid) self.rest.permissions.require.sysadmin_or_self(cid)
...@@ -81,6 +84,7 @@ class ClientSecretController(CustomResource): ...@@ -81,6 +84,7 @@ class ClientSecretController(CustomResource):
return '', 204 return '', 204
@jwt_required @jwt_required
@access_log
@clients_namespace.response(204, 'Client secret updated') @clients_namespace.response(204, 'Client secret updated')
@clients_namespace.response(403, 'Not allowed to update client secret') @clients_namespace.response(403, 'Not allowed to update client secret')
def put(self, cid: str, sid: str): def put(self, cid: str, sid: str):
......
...@@ -9,6 +9,7 @@ from portal.rest.custom_resource import CustomResource ...@@ -9,6 +9,7 @@ from portal.rest.custom_resource import CustomResource
from portal.rest.schemas import SCHEMAS from portal.rest.schemas import SCHEMAS
from portal.service.errors import ForbiddenError, PortalAPIError from portal.service.errors import ForbiddenError, PortalAPIError
from portal.service.filters import filter_course_dump from portal.service.filters import filter_course_dump
from portal.tools.decorators import access_log
courses_namespace = Namespace('courses') courses_namespace = Namespace('courses')
log = logger.get_logger(__name__) log = logger.get_logger(__name__)
...@@ -29,6 +30,7 @@ class CourseList(CustomResource): ...@@ -29,6 +30,7 @@ class CourseList(CustomResource):
@jwt_required @jwt_required
# @courses_namespace.response(200, 'Created course', model=course_schema) # @courses_namespace.response(200, 'Created course', model=course_schema)
# @courses_namespace.response(403, 'Not allowed to create course', model=course_schema) # @courses_namespace.response(403, 'Not allowed to create course', model=course_schema)
@access_log
def post(self): def post(self):
self.permissions.require.sysadmin() self.permissions.require.sysadmin()
...@@ -62,6 +64,7 @@ class CourseResource(CustomResource): ...@@ -62,6 +64,7 @@ class CourseResource(CustomResource):
raise ForbiddenError(client=client) raise ForbiddenError(client=client)
@jwt_required @jwt_required
@access_log
@courses_namespace.response(204, 'Course deleted') @courses_namespace.response(204, 'Course deleted')
@courses_namespace.response(403, 'Not allowed to delete course') @courses_namespace.response(403, 'Not allowed to delete course')
def delete(self, cid: str): def delete(self, cid: str):
...@@ -71,6 +74,7 @@ class CourseResource(CustomResource): ...@@ -71,6 +74,7 @@ class CourseResource(CustomResource):
return '', 204 return '', 204
@jwt_required @jwt_required
@access_log
@courses_namespace.response(204, 'Course updated') @courses_namespace.response(204, 'Course updated')
@courses_namespace.response(403, 'Not allowed to update course') @courses_namespace.response(403, 'Not allowed to update course')
def put(self, cid: str): def put(self, cid: str):
...@@ -98,6 +102,7 @@ class CourseNotesToken(CustomResource): ...@@ -98,6 +102,7 @@ class CourseNotesToken(CustomResource):
return course.notes_access_token return course.notes_access_token
@jwt_required @jwt_required
@access_log
@courses_namespace.response(204, 'Course\'s notes access token updated') @courses_namespace.response(204, 'Course\'s notes access token updated')
@courses_namespace.response( @courses_namespace.response(
403, 'Not allowed to update course\'s notes access token') 403, 'Not allowed to update course\'s notes access token')
...@@ -117,6 +122,7 @@ class CourseNotesToken(CustomResource): ...@@ -117,6 +122,7 @@ class CourseNotesToken(CustomResource):
@courses_namespace.response(404, 'Course not found') @courses_namespace.response(404, 'Course not found')
class CourseImport(CustomResource): class CourseImport(CustomResource):
@jwt_required @jwt_required
@access_log
# @courses_namespace.response(200, 'Course has been imported', model=course_schema) # @courses_namespace.response(200, 'Course has been imported', model=course_schema)
@courses_namespace.response(403, 'Not allowed to import course') @courses_namespace.response(403, 'Not allowed to import course')
@courses_namespace.response(400, 'Cannot import course to itself.') @courses_namespace.response(400, 'Cannot import course to itself.')
......
...@@ -6,6 +6,7 @@ from portal import logger ...@@ -6,6 +6,7 @@ from portal import logger
from portal.rest import rest_helpers from portal.rest import rest_helpers
from portal.rest.custom_resource import CustomResource from portal.rest.custom_resource import CustomResource
from portal.rest.schemas import SCHEMAS from portal.rest.schemas import SCHEMAS
from portal.tools.decorators import access_log
groups_namespace = Namespace('') groups_namespace = Namespace('')
...@@ -24,6 +25,7 @@ class GroupsList(CustomResource): ...@@ -24,6 +25,7 @@ class GroupsList(CustomResource):
return SCHEMAS.dump('groups', groups) return SCHEMAS.dump('groups', groups)
@jwt_required @jwt_required
@access_log
# @groups_namespace.response(201, 'Group created', model=group_schema) # @groups_namespace.response(201, 'Group created', model=group_schema)
@groups_namespace.response(403, 'Not allowed to create group') @groups_namespace.response(403, 'Not allowed to create group')
def post(self, cid: str): def post(self, cid: str):
...@@ -55,6 +57,7 @@ class GroupResource(CustomResource): ...@@ -55,6 +57,7 @@ class GroupResource(CustomResource):
return SCHEMAS.dump('group', group) return SCHEMAS.dump('group', group)
@jwt_required @jwt_required
@access_log
@groups_namespace.response(204, 'Group deleted') @groups_namespace.response(204, 'Group deleted')
@groups_namespace.response(403, 'Not allowed to delete group') @groups_namespace.response(403, 'Not allowed to delete group')
def delete(self, cid: str, gid: str): def delete(self, cid: str, gid: str):
...@@ -67,6 +70,7 @@ class GroupResource(CustomResource): ...@@ -67,6 +70,7 @@ class GroupResource(CustomResource):
return '', 204 return '', 204
@jwt_required @jwt_required
@access_log
@groups_namespace.response(204, 'Group updated') @groups_namespace.response(204, 'Group updated')
@groups_namespace.response(403, 'Not allowed to update group') @groups_namespace.response(403, 'Not allowed to update group')
def put(self, cid: str, gid: str): def put(self, cid: str, gid: str):
...@@ -99,6 +103,7 @@ class GroupUsersList(CustomResource): ...@@ -99,6 +103,7 @@ class GroupUsersList(CustomResource):
return SCHEMAS.dump('users', users) return SCHEMAS.dump('users', users)
@jwt_required @jwt_required
@access_log
@groups_namespace.response(204, 'User\'s list updated') @groups_namespace.response(204, 'User\'s list updated')
@groups_namespace.response(403, 'Not allowed to add users to the group') @groups_namespace.response(403, 'Not allowed to add users to the group')
def put(self, cid: str, gid: str): def put(self, cid: str, gid: str):
...@@ -127,6 +132,7 @@ class GroupUsersList(CustomResource): ...@@ -127,6 +132,7 @@ class GroupUsersList(CustomResource):
@groups_namespace.response(404, 'User not found') @groups_namespace.response(404, 'User not found')
class GroupAddOrDeleteSingleUser(CustomResource): class GroupAddOrDeleteSingleUser(CustomResource):
@jwt_required @jwt_required
@access_log
@groups_namespace.response(204, 'User\'s list updated') @groups_namespace.response(204, 'User\'s list updated')
@groups_namespace.response(403, 'Not allowed to add user to the group') @groups_namespace.response(403, 'Not allowed to add user to the group')
def put(self, cid: str, gid: str, uid: str): def put(self, cid: str, gid: str, uid: str):
...@@ -141,6 +147,7 @@ class GroupAddOrDeleteSingleUser(CustomResource): ...@@ -141,6 +147,7 @@ class GroupAddOrDeleteSingleUser(CustomResource):
return '', 204 return '', 204
@jwt_required @jwt_required
@access_log
@groups_namespace.response(204, 'User\'s list updated') @groups_namespace.response(204, 'User\'s list updated')
@groups_namespace.response(403, 'Not allowed to delete users to the group') @groups_namespace.response(403, 'Not allowed to delete users to the group')
def delete(self, cid: str, gid: str, uid: str): def delete(self, cid: str, gid: str, uid: str):
...@@ -181,6 +188,7 @@ class GroupProjectsList(CustomResource): ...@@ -181,6 +188,7 @@ class GroupProjectsList(CustomResource):
@groups_namespace.response(404, 'Project not found') @groups_namespace.response(404, 'Project not found')
class GroupAddOrDeleteProject(CustomResource): class GroupAddOrDeleteProject(CustomResource):
@jwt_required @jwt_required
@access_log
@groups_namespace.response(204, 'Projects\'s list updated') @groups_namespace.response(204, 'Projects\'s list updated')
@groups_namespace.response(403, 'Not allowed to add project to the group') @groups_namespace.response(403, 'Not allowed to add project to the group')
def put(self, cid: str, gid: str, pid: str): def put(self, cid: str, gid: str, pid: str):
...@@ -195,6 +203,7 @@ class GroupAddOrDeleteProject(CustomResource): ...@@ -195,6 +203,7 @@ class GroupAddOrDeleteProject(CustomResource):
return '', 204 return '', 204
@jwt_required @jwt_required
@access_log
@groups_namespace.response(204, 'Projects\'s list updated') @groups_namespace.response(204, 'Projects\'s list updated')
@groups_namespace.response( @groups_namespace.response(
403, 'Not allowed to delete project from the group') 403, 'Not allowed to delete project from the group')
...@@ -215,6 +224,7 @@ class GroupAddOrDeleteProject(CustomResource): ...@@ -215,6 +224,7 @@ class GroupAddOrDeleteProject(CustomResource):
@groups_namespace.response(404, 'Course not found') @groups_namespace.response(404, 'Course not found')
class GroupImport(CustomResource): class GroupImport(CustomResource):
@jwt_required @jwt_required
@access_log
@groups_namespace.response(201, 'Group import') @groups_namespace.response(201, 'Group import')
@groups_namespace.response(404, 'Group not found') @groups_namespace.response(404, 'Group not found')
@groups_namespace.response(403, 'Not allowed to import to the group') @groups_namespace.response(403, 'Not allowed to import to the group')
......
...@@ -8,6 +8,7 @@ from portal.rest import rest_helpers ...@@ -8,6 +8,7 @@ from portal.rest import rest_helpers
from portal.rest.custom_resource import CustomResource from portal.rest.custom_resource import CustomResource
from portal.rest.schemas import SCHEMAS from portal.rest.schemas import SCHEMAS
from portal.service import errors from portal.service import errors
from portal.tools.decorators import access_log
projects_namespace = Namespace('') # pylint: disable=invalid-name projects_namespace = Namespace('') # pylint: disable=invalid-name
...@@ -27,6 +28,7 @@ class ProjectsList(CustomResource): ...@@ -27,6 +28,7 @@ class ProjectsList(CustomResource):
return SCHEMAS.dump('projects', projects) return SCHEMAS.dump('projects', projects)
@jwt_required @jwt_required
@access_log
@projects_namespace.response(404, 'Course not found') @projects_namespace.response(404, 'Course not found')
# @projects_namespace.response(201, 'Project created', model=project_schema) # @projects_namespace.response(201, 'Project created', model=project_schema)
def post(self, cid: str): def post(self, cid: str):
...@@ -58,6 +60,7 @@ class ProjectResource(CustomResource): ...@@ -58,6 +60,7 @@ class ProjectResource(CustomResource):
return SCHEMAS.dump('project', project) return SCHEMAS.dump('project', project)
@jwt_required @jwt_required
@access_log
@projects_namespace.response(204, 'Project deleted') @projects_namespace.response(204, 'Project deleted')
@projects_namespace.response(403, 'Not allowed to delete project') @projects_namespace.response(403, 'Not allowed to delete project')
def delete(self, cid: str, pid: str): def delete(self, cid: str, pid: str):
...@@ -70,6 +73,7 @@ class ProjectResource(CustomResource): ...@@ -70,6 +73,7 @@ class ProjectResource(CustomResource):
return '', 204 return '', 204
@jwt_required @jwt_required
@access_log
@projects_namespace.response(204, 'Project updated') @projects_namespace.response(204, 'Project updated')
@projects_namespace.response(403, 'Not allowed to update project') @projects_namespace.response(403, 'Not allowed to update project')
def put(self, cid: str, pid: str): def put(self, cid: str, pid: str):
...@@ -110,6 +114,7 @@ class ProjectConfigResource(CustomResource): ...@@ -110,6 +114,7 @@ class ProjectConfigResource(CustomResource):
raise errors.ForbiddenError(perm_service.client) raise errors.ForbiddenError(perm_service.client)
@jwt_required @jwt_required
@access_log
@projects_namespace.response(204, 'Project config updated') @projects_namespace.response(204, 'Project config updated')
def put(self, cid: str, pid: str): def put(self, cid: str, pid: str):
course = self.find.course(cid) course = self.find.course(cid)
...@@ -129,6 +134,7 @@ class ProjectConfigResource(CustomResource): ...@@ -129,6 +134,7 @@ class ProjectConfigResource(CustomResource):
@projects_namespace.response(404, 'Project not found') @projects_namespace.response(404, 'Project not found')
class ProjectTestFilesRefresh(CustomResource): class ProjectTestFilesRefresh(CustomResource):
@jwt_required @jwt_required
@access_log
@projects_namespace.response(204, 'Project test_files updated') @projects_namespace.response(204, 'Project test_files updated')
def post(self, cid: str, pid: str): def post(self, cid: str, pid: str):
course = self.find.course(cid) course = self.find.course(cid)
...@@ -160,6 +166,7 @@ class ProjectSubmissions(CustomResource): ...@@ -160,6 +166,7 @@ class ProjectSubmissions(CustomResource):
return SCHEMAS.dump('submissions', submissions) return SCHEMAS.dump('submissions', submissions)
@jwt_required @jwt_required
@access_log
# @projects_namespace.response(201, 'Project submission create', model=submission_schema) # @projects_namespace.response(201, 'Project submission create', model=submission_schema)
def post(self, cid: str, pid: str): def post(self, cid: str, pid: str):
client = self.rest.auth.client client = self.rest.auth.client
......
...@@ -7,6 +7,7 @@ from portal.database.models import ClientType ...@@ -7,6 +7,7 @@ from portal.database.models import ClientType
from portal.rest import rest_helpers from portal.rest import rest_helpers
from portal.rest.custom_resource import CustomResource from portal.rest.custom_resource import CustomResource
from portal.rest.schemas import SCHEMAS from portal.rest.schemas import SCHEMAS
from portal.tools.decorators import access_log
roles_namespace = Namespace('') roles_namespace = Namespace('')
...@@ -24,6 +25,7 @@ class RoleList(CustomResource): ...@@ -24,6 +25,7 @@ class RoleList(CustomResource):
return SCHEMAS.dump('roles', roles) return SCHEMAS.dump('roles', roles)
@jwt_required @jwt_required
@access_log
# @roles_namespace.response(201, 'Role created', model=role_schema) # @roles_namespace.response(201, 'Role created', model=role_schema)
def post(self, cid): def post(self, cid):
course = self.find.course(cid) course = self.find.course(cid)
...@@ -52,6 +54,7 @@ class RoleResource(CustomResource): ...@@ -52,6 +54,7 @@ class RoleResource(CustomResource):
return SCHEMAS.dump('role', role) return SCHEMAS.dump('role', role)
@jwt_required @jwt_required
@access_log
@roles_namespace.response(204, 'Deleted role') @roles_namespace.response(204, 'Deleted role')
def delete(self, cid, rid): def delete(self, cid, rid):
course = self.find.course(cid) course = self.find.course(cid)
...@@ -63,6 +66,7 @@ class RoleResource(CustomResource): ...@@ -63,6 +66,7 @@ class RoleResource(CustomResource):
return '', 204 return '', 204
@jwt_required @jwt_required
@access_log
@roles_namespace.response(204, 'Role updated') @roles_namespace.response(204, 'Role updated')
def put(self, cid: str, rid: str): def put(self, cid: str, rid: str):
course = self.find.course(cid) course = self.find.course(cid)
...@@ -94,6 +98,7 @@ class RolePermissions(CustomResource): ...@@ -94,6 +98,7 @@ class RolePermissions(CustomResource):
return SCHEMAS.dump('permissions', role.permissions) return SCHEMAS.dump('permissions', role.permissions)
@jwt_required @jwt_required
@access_log
# @roles_namespace.response(204, 'Update permissions for course', model=permissions_schema) # @roles_namespace.response(204, 'Update permissions for course', model=permissions_schema)
def put(self, cid: str, rid: str): def put(self, cid: str, rid: str):
course = self.find.course(cid) course = self.find.course(cid)
...@@ -127,6 +132,7 @@ class RoleUsersList(CustomResource): ...@@ -127,6 +132,7 @@ class RoleUsersList(CustomResource):
return SCHEMAS.dump('users', users) return SCHEMAS.dump('users', users)
@jwt_required @jwt_required
@access_log
@roles_namespace.response(204, 'Updates users membership in role') @roles_namespace.response(204, 'Updates users membership in role')
def put(self, cid: str, rid: str): def put(self, cid: str, rid: str):
course = self.find.course(cid) course = self.find.course(cid)
...@@ -154,6 +160,7 @@ class RoleUsersList(CustomResource): ...@@ -154,6 +160,7 @@ class RoleUsersList(CustomResource):
@roles_namespace.response(404, 'Client not found') @roles_namespace.response(404, 'Client not found')
class RoleClient(CustomResource): class RoleClient(CustomResource):
@jwt_required @jwt_required
@access_log
@roles_namespace.response(204, 'Adds permissions to role') @roles_namespace.response(204, 'Adds permissions to role')
def put(self, cid: str, rid: str, clid: str): def put(self, cid: str, rid: str, clid: str):
course = self.find.course(cid) course = self.find.course(cid)
...@@ -166,6 +173,7 @@ class RoleClient(CustomResource): ...@@ -166,6 +173,7 @@ class RoleClient(CustomResource):
return '', 204 return '', 204
@jwt_required @jwt_required
@access_log
@roles_namespace.response(204, 'Removes permissions from role') @roles_namespace.response(204, 'Removes permissions from role')
def delete(self, cid: str, rid: str, clid: str): def delete(self, cid: str, rid: str, clid: str):
course = self.find.course(cid) course = self.find.course(cid)
......
...@@ -5,6 +5,7 @@ from portal import logger, storage ...@@ -5,6 +5,7 @@ from portal import logger, storage
from portal.rest import rest_helpers from portal.rest import rest_helpers
from portal.rest.custom_resource import CustomResource from portal.rest.custom_resource import CustomResource
from portal.rest.schemas import SCHEMAS from portal.rest.schemas import SCHEMAS
from portal.tools.decorators import access_log
submissions_namespace = Namespace('submissions') submissions_namespace = Namespace('submissions')
...@@ -33,6 +34,7 @@ class SubmissionResource(CustomResource): ...@@ -33,6 +34,7 @@ class SubmissionResource(CustomResource):
return SCHEMAS.dump('submission', submission) return SCHEMAS.dump('submission', submission)
@jwt_required @jwt_required
@access_log
@submissions_namespace.response(204, 'Submission deleted') @submissions_namespace.response(204, 'Submission deleted')
def delete(self, sid: str): def delete(self, sid: str):
self.permissions.require.sysadmin() self.permissions.require.sysadmin()
...@@ -54,6 +56,7 @@ class SubmissionState(CustomResource): ...@@ -54,6 +56,7 @@ class SubmissionState(CustomResource):
return SCHEMAS.dump('submission', submission) return SCHEMAS.dump('submission', submission)
@jwt_required @jwt_required
@access_log
@submissions_namespace.response(204, 'Submission state updated') @submissions_namespace.response(204, 'Submission state updated')
def put(self, sid: str): def put(self, sid: str):
client = self.rest.auth.client client = self.rest.auth.client
...@@ -169,6 +172,7 @@ class SubmissionResultFiles(CustomResource): ...@@ -169,6 +172,7 @@ class SubmissionResultFiles(CustomResource):
return service.send_file_or_zip(storage_entity) return service.send_file_or_zip(storage_entity)
@jwt_required @jwt_required
@access_log
def post(self, sid: str): def post(self, sid: str):
submission = self.find.submission(sid) submission = self.find.submission(sid)
# authorization # authorization
...@@ -185,6 +189,7 @@ class SubmissionResultFiles(CustomResource): ...@@ -185,6 +189,7 @@ class SubmissionResultFiles(CustomResource):
@submissions_namespace.response(404, 'Submissions not found') @submissions_namespace.response(404, 'Submissions not found')
class SubmissionResubmit(CustomResource): class SubmissionResubmit(CustomResource):
@jwt_required @jwt_required
@access_log