Implementing Render Route & Security Checks for Attendee Tickets

This blog post explains the requirements & implementation details of a secure route over which the tickets could be served in the Open Event Project (Eventyay). Eventyay is the Open Event management solution using standardized event formats developed at FOSSASIA. Sometimes, tickets of a user can be utilized in the process of fraudulent actions. To prevent this, security is of the utmost importance.

Prior to this feature, anonymous/unauthorized users were able to access the tickets which belonged to another user with a simple link. There was no provision of any authentication check.  An additional problem with the tickets were the storage methodology where the tickets were stored in a top-level folder which was not protected. Therefore, there was a necessity to implement a flask route which could check if the user was authenticated, the ticket belonged to the authorized user/admin/organizer of the event and provide proper exceptions in other cases. 

Ticket completion page with option to download tickets

When the user places an order and it goes through successfully,the ticket is generated, stored in a protected folder and the user is redirected to the order completion page where they would be able to download their tickets. When the user clicks on the Download Tickets Button, the ticket_blueprint route is triggered.

@ticket_blueprint.route('/tickets/<string:order_identifier>')
@jwt_required
def ticket_attendee_authorized(order_identifier):
    if current_user:
        try:
            order = Order.query.filter_by(identifier=order_identifier).first()
        except NoResultFound:
            return NotFoundError({'source': ''}, 'This ticket is not associated with any order').respond()
        if current_user.can_download_tickets(order):
            key = UPLOAD_PATHS['pdf']['tickets_all'].format(identifier=order_identifier)
            file_path = '../generated/tickets/{}/{}/'.format(key, generate_hash(key)) + order_identifier + '.pdf'
            try:
                return return_file('ticket', file_path, order_identifier)
            except FileNotFoundError:
                create_pdf_tickets_for_holder(order)
                return return_file('ticket', file_path, order_identifier)
        else:
            return ForbiddenError({'source': ''}, 'Unauthorized Access').respond()
    else:
        return ForbiddenError({'source': ''}, 'Authentication Required to access ticket').respond()

                         tickets_route – the logic pertaining to security module for attendee tickets

The function associated with the ticket downloads queries the Order model using the order identifier as a key. Then, it checks if the current authenticated user is either a staff member, the owner of the ticket or the organizer of the ticket. If it passes this check, the file path is generated and tickets are downloaded using the return_tickets function.

In the return_tickets function, we utilize the send_file function imported from Flask and wrap it with flaskā€™s make_response function. In addition to that, we attach headers to specify that it is an attachment and add an appropriate name to it.

def return_file(file_name_prefix, file_path, identifier):
    response = make_response(send_file(file_path))
    response.headers['Content-Disposition'] = 'attachment; filename=%s-%s.pdf' % (file_name_prefix, identifier)
    return response

return_tickets function – sends the file as a make_response with appropriate headers

When it comes to exception handling, at each stage whenever a ticket is not to be found while querying or the authentication check fails, a proper exception is thrown to the user. For example, at the step where an attempt is made to return the file using file path after authentication, if the tickets are NotFound, the tickets are generated on the fly. 

 def can_download_tickets(self, order):
        permissible_users = [holder.id for holder in order.ticket_holders] + [order.user.id]
        if self.is_staff or self.has_event_access(order.event.id) or self.id in permissible_users:
            return True
        return False

can_download_tickets – check for proper ticket access 

Resources:

Related work and code repo:

Tags:

Eventyay, FOSSASIA, Flask, SQLAlchemy, Open Event, Python, JWT

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.