Repository: fossasia/open-event-server

Issue:
Check Authorization level before exporting an event.
**Describe the bug**
Export event has some information which should not be available to a normal user. 

**Expected behavior**
Only the coorganizer and the above roles should be able to export an event.

**Additional context**
Working on it.


Discussion:
@prateekj117 The FE PR is ready for this https://github.com/fossasia/open-event-frontend/pull/3606

Resolving patch:
diff --git a/app/api/exports.py b/app/api/exports.py
--- a/app/api/exports.py
+++ b/app/api/exports.py
@@ -2,12 +2,11 @@
 
 from flask import send_file, make_response, jsonify, url_for, \
     current_app, request, Blueprint
-from flask_jwt_extended import jwt_required, current_user
+from flask_jwt_extended import current_user
 
 from app.api.helpers.export_helpers import export_event_json, create_export_job
+from app.api.helpers.permissions import is_coorganizer, to_event_id
 from app.api.helpers.utilities import TASK_RESULTS
-from app.models import db
-from app.models.event import Event
 
 export_routes = Blueprint('exports', __name__, url_prefix='/v1')
 
@@ -19,9 +18,10 @@
 }
 
 
-@export_routes.route('/events/<string:event_identifier>/export/json', methods=['POST'])
-@jwt_required
-def export_event(event_identifier):
+@export_routes.route('/events/<string:event_identifier>/export/json', methods=['POST'], endpoint='export_event')
+@to_event_id
+@is_coorganizer
+def export_event(event_id):
     from .helpers.tasks import export_event_task
 
     settings = EXPORT_SETTING
@@ -30,11 +30,6 @@ def export_event(event_identifier):
     settings['document'] = request.json.get('document', False)
     settings['audio'] = request.json.get('audio', False)
 
-    if not event_identifier.isdigit():
-        event = db.session.query(Event).filter_by(identifier=event_identifier).first()
-        event_id = event.id
-    else:
-        event_id = event_identifier
     # queue task
     task = export_event_task.delay(
         current_user.email, event_id, settings)
@@ -53,8 +48,9 @@ def export_event(event_identifier):
     )
 
 
-@export_routes.route('/events/<string:event_id>/exports/<path:path>')
-@jwt_required
+@export_routes.route('/events/<string:event_id>/exports/<path:path>', endpoint='export_download')
+@to_event_id
+@is_coorganizer
 def export_download(event_id, path):
     if not path.startswith('/'):
         path = '/' + path
@@ -65,15 +61,10 @@ def export_download(event_id, path):
     return response
 
 
-@export_routes.route('/events/<string:event_identifier>/export/xcal', methods=['GET'])
-@jwt_required
-def export_event_xcal(event_identifier):
-
-    if not event_identifier.isdigit():
-        event = db.session.query(Event).filter_by(identifier=event_identifier).first()
-        event_id = str(event.id)
-    else:
-        event_id = event_identifier
+@export_routes.route('/events/<string:event_identifier>/export/xcal', methods=['GET'], endpoint='export_event_xcal')
+@to_event_id
+@is_coorganizer
+def export_event_xcal(event_id):
 
     from .helpers.tasks import export_xcal_task
 
@@ -94,15 +85,10 @@ def event_export_task_base(event_id, settings):
     return path
 
 
-@export_routes.route('/events/<string:event_identifier>/export/ical', methods=['GET'])
-@jwt_required
-def export_event_ical(event_identifier):
-    if not event_identifier.isdigit():
-        event = db.session.query(Event).filter_by(identifier=event_identifier).first()
-        event_id = str(event.id)
-    else:
-        event_id = event_identifier
-
+@export_routes.route('/events/<string:event_identifier>/export/ical', methods=['GET'], endpoint='export_event_ical')
+@to_event_id
+@is_coorganizer
+def export_event_ical(event_id):
     from .helpers.tasks import export_ical_task
 
     task = export_ical_task.delay(event_id)
@@ -114,15 +100,11 @@ def export_event_ical(event_identifier):
     )
 
 
-@export_routes.route('/events/<string:event_identifier>/export/pentabarf', methods=['GET'])
-@jwt_required
-def export_event_pentabarf(event_identifier):
-    if not event_identifier.isdigit():
-        event = db.session.query(Event).filter_by(identifier=event_identifier).first()
-        event_id = str(event.id)
-    else:
-        event_id = event_identifier
-
+@export_routes.route('/events/<string:event_identifier>/export/pentabarf', methods=['GET'],
+                     endpoint='export_event_pentabarf')
+@to_event_id
+@is_coorganizer
+def export_event_pentabarf(event_id):
     from .helpers.tasks import export_pentabarf_task
 
     task = export_pentabarf_task.delay(event_id)
@@ -134,15 +116,11 @@ def export_event_pentabarf(event_identifier):
     )
 
 
-@export_routes.route('/events/<string:event_identifier>/export/orders/csv', methods=['GET'])
-@jwt_required
-def export_orders_csv(event_identifier):
-    if not event_identifier.isdigit():
-        event = db.session.query(Event).filter_by(identifier=event_identifier).first()
-        event_id = str(event.id)
-    else:
-        event_id = event_identifier
-
+@export_routes.route('/events/<string:event_identifier>/export/orders/csv', methods=['GET'],
+                     endpoint='export_orders_csv')
+@to_event_id
+@is_coorganizer
+def export_orders_csv(event_id):
     from .helpers.tasks import export_order_csv_task
 
     task = export_order_csv_task.delay(event_id)
@@ -154,15 +132,11 @@ def export_orders_csv(event_identifier):
     )
 
 
-@export_routes.route('/events/<string:event_identifier>/export/orders/pdf', methods=['GET'])
-@jwt_required
-def export_orders_pdf(event_identifier):
-    if not event_identifier.isdigit():
-        event = db.session.query(Event).filter_by(identifier=event_identifier).first()
-        event_id = str(event.id)
-    else:
-        event_id = event_identifier
-
+@export_routes.route('/events/<string:event_identifier>/export/orders/pdf', methods=['GET'],
+                     endpoint='export_orders_pdf')
+@to_event_id
+@is_coorganizer
+def export_orders_pdf(event_id):
     from .helpers.tasks import export_order_pdf_task
 
     task = export_order_pdf_task.delay(event_id)
@@ -174,15 +148,11 @@ def export_orders_pdf(event_identifier):
     )
 
 
-@export_routes.route('/events/<string:event_identifier>/export/attendees/csv', methods=['GET'])
-@jwt_required
-def export_attendees_csv(event_identifier):
-    if not event_identifier.isdigit():
-        event = db.session.query(Event).filter_by(identifier=event_identifier).first()
-        event_id = str(event.id)
-    else:
-        event_id = event_identifier
-
+@export_routes.route('/events/<string:event_identifier>/export/attendees/csv', methods=['GET'],
+                     endpoint='export_attendees_csv')
+@to_event_id
+@is_coorganizer
+def export_attendees_csv(event_id):
     from .helpers.tasks import export_attendees_csv_task
 
     task = export_attendees_csv_task.delay(event_id)
@@ -194,15 +164,11 @@ def export_attendees_csv(event_identifier):
     )
 
 
-@export_routes.route('/events/<string:event_identifier>/export/attendees/pdf', methods=['GET'])
-@jwt_required
-def export_attendees_pdf(event_identifier):
-    if not event_identifier.isdigit():
-        event = db.session.query(Event).filter_by(identifier=event_identifier).first()
-        event_id = str(event.id)
-    else:
-        event_id = event_identifier
-
+@export_routes.route('/events/<string:event_identifier>/export/attendees/pdf', methods=['GET'],
+                     endpoint='export_attendees_pdf')
+@to_event_id
+@is_coorganizer
+def export_attendees_pdf(event_id):
     from .helpers.tasks import export_attendees_pdf_task
 
     task = export_attendees_pdf_task.delay(event_id)
@@ -214,15 +180,11 @@ def export_attendees_pdf(event_identifier):
     )
 
 
-@export_routes.route('/events/<string:event_identifier>/export/sessions/csv', methods=['GET'])
-@jwt_required
-def export_sessions_csv(event_identifier):
-    if not event_identifier.isdigit():
-        event = db.session.query(Event).filter_by(identifier=event_identifier).first()
-        event_id = str(event.id)
-    else:
-        event_id = event_identifier
-
+@export_routes.route('/events/<string:event_identifier>/export/sessions/csv', methods=['GET'],
+                     endpoint='export_sessions_csv')
+@to_event_id
+@is_coorganizer
+def export_sessions_csv(event_id):
     from .helpers.tasks import export_sessions_csv_task
 
     task = export_sessions_csv_task.delay(event_id)
@@ -234,15 +196,11 @@ def export_sessions_csv(event_identifier):
     )
 
 
-@export_routes.route('/events/<string:event_identifier>/export/speakers/csv', methods=['GET'])
-@jwt_required
-def export_speakers_csv(event_identifier):
-    if not event_identifier.isdigit():
-        event = db.session.query(Event).filter_by(identifier=event_identifier).first()
-        event_id = str(event.id)
-    else:
-        event_id = event_identifier
-
+@export_routes.route('/events/<string:event_identifier>/export/speakers/csv', methods=['GET'],
+                     endpoint='export_speakers_csv')
+@to_event_id
+@is_coorganizer
+def export_speakers_csv(event_id):
     from .helpers.tasks import export_speakers_csv_task
 
     task = export_speakers_csv_task.delay(event_id)
@@ -254,15 +212,11 @@ def export_speakers_csv(event_identifier):
     )
 
 
-@export_routes.route('/events/<string:event_identifier>/export/sessions/pdf', methods=['GET'])
-@jwt_required
-def export_sessions_pdf(event_identifier):
-    if not event_identifier.isdigit():
-        event = db.session.query(Event).filter_by(identifier=event_identifier).first()
-        event_id = str(event.id)
-    else:
-        event_id = event_identifier
-
+@export_routes.route('/events/<string:event_identifier>/export/sessions/pdf', methods=['GET'],
+                     endpoint='export_sessions_pdf')
+@to_event_id
+@is_coorganizer
+def export_sessions_pdf(event_id):
     from .helpers.tasks import export_sessions_pdf_task
 
     task = export_sessions_pdf_task.delay(event_id)
@@ -274,15 +228,11 @@ def export_sessions_pdf(event_identifier):
     )
 
 
-@export_routes.route('/events/<string:event_identifier>/export/speakers/pdf', methods=['GET'])
-@jwt_required
-def export_speakers_pdf(event_identifier):
-    if not event_identifier.isdigit():
-        event = db.session.query(Event).filter_by(identifier=event_identifier).first()
-        event_id = str(event.id)
-    else:
-        event_id = event_identifier
-
+@export_routes.route('/events/<string:event_identifier>/export/speakers/pdf', methods=['GET'],
+                     endpoint='export_speakers_pdf')
+@to_event_id
+@is_coorganizer
+def export_speakers_pdf(event_id):
     from .helpers.tasks import export_speakers_pdf_task
 
     task = export_speakers_pdf_task.delay(event_id)
diff --git a/app/api/helpers/permissions.py b/app/api/helpers/permissions.py
--- a/app/api/helpers/permissions.py
+++ b/app/api/helpers/permissions.py
@@ -1,11 +1,12 @@
 from functools import wraps
-from flask import current_app as app
 from flask_jwt_extended import verify_jwt_in_request, current_user
 
 from app.api.helpers.db import save_to_db
 from app.api.helpers.errors import ForbiddenError
 from flask import request
 from datetime import datetime
+from app.models import db
+from app.models.event import Event
 
 
 def second_order_decorator(inner_dec):
@@ -145,6 +146,30 @@ def decorated_function(*args, **kwargs):
     return decorated_function
 
 
+@second_order_decorator(jwt_required)
+def to_event_id(func):
+    """
+    Change event_identifier to event_id in kwargs
+    :param f:
+    :return:
+    """
+
+    @wraps(func)
+    def decorated_function(*args, **kwargs):
+
+        if 'event_identifier' in kwargs:
+            if not kwargs['event_identifier'].isdigit():
+                event = db.session.query(Event).filter_by(identifier=kwargs['event_identifier']).first()
+                kwargs['event_id'] = event.id
+            else:
+                kwargs['event_id'] = kwargs['event_identifier']
+
+        return func(*args, **kwargs)
+
+    return decorated_function
+
+
+
 @second_order_decorator(jwt_required)
 def is_coorganizer(f):
     """
@@ -157,9 +182,9 @@ def is_coorganizer(f):
     def decorated_function(*args, **kwargs):
         user = current_user
 
-        if user.is_staff:
-            return f(*args, **kwargs)
-        if 'event_id' in kwargs and user.has_event_access(kwargs['event_id']):
+        if user.is_staff or ('event_id' in kwargs and user.has_event_access(kwargs['event_id'])):
+            if 'event_identifier' in kwargs:
+                kwargs.pop('event_identifier', None)
             return f(*args, **kwargs)
         return ForbiddenError({'source': ''}, 'Co-organizer access is required.').respond()
 


Test patch: