Beispiel #1
0
def create_message(session, tid, wbtip_id, receiver_id, content):
    wbtip, itip, rtip_id = db_get(session,
                                  (models.WhistleblowerTip, models.InternalTip, models.ReceiverTip.id),
                                  (models.WhistleblowerTip.id == wbtip_id,
                                   models.ReceiverTip.internaltip_id == wbtip_id,
                                   models.ReceiverTip.receiver_id == receiver_id,
                                   models.InternalTip.id == models.WhistleblowerTip.id,
                                   models.InternalTip.enable_two_way_messages.is_(True),
                                   models.InternalTip.status != 'closed',
                                   models.InternalTip.tid == tid))

    itip.update_date = itip.wb_last_access = datetime_now()

    _content = content
    if itip.crypto_tip_pub_key:
        _content = base64.b64encode(GCE.asymmetric_encrypt(itip.crypto_tip_pub_key, content)).decode()

    msg = models.Message()
    msg.receivertip_id = rtip_id
    msg.type = 'whistleblower'
    msg.content = _content
    session.add(msg)
    session.flush()

    ret = serialize_message(session, msg)
    ret['content'] = content

    return ret
Beispiel #2
0
def db_update_node(session, tid, user_session, request, language):
    """
    Transaction to update the node configuration

    :param session: An ORM session
    :param tid: A tenant ID
    :param user_session: The current user session
    :param request: The request data
    :param language: the language in which to localize data
    :return: Return the serialized configuration for the specified tenant
    """
    config = ConfigFactory(session, tid)

    enable_escrow = not config.get_val('escrow') and request.get('escrow', False)
    disable_escrow = user_session.ek and config.get_val('escrow') and not request.get('escrow', False)

    config.update('node', request)

    if request['enable_ricochet_panel'] and not request['ricochet_address']:
        request['enable_ricochet_panel'] = False

    # Validate that IP addresses/ranges we're getting are goo
    if 'ip_filter_admin' in request and request['ip_filter_admin_enable'] and request['ip_filter_admin']:
        parse_csv_ip_ranges_to_ip_networks(request['ip_filter_admin'])

    if 'languages_enabled' in request and 'default_language' in request:
        db_update_enabled_languages(session,
                                    tid,
                                    request['languages_enabled'],
                                    request['default_language'])

    if language in db_get_languages(session, tid):
        ConfigL10NFactory(session, tid).update('node', request, language)

    if enable_escrow:
        crypto_escrow_prv_key, State.tenant_cache[tid].crypto_escrow_pub_key = GCE.generate_keypair()
        user = db_get(session, models.User, models.User.id == user_session.user_id)
        user.crypto_escrow_prv_key = Base64Encoder.encode(GCE.asymmetric_encrypt(user.crypto_pub_key, crypto_escrow_prv_key))

        if tid == 1:
            session.query(models.User).update({'password_change_needed': True}, synchronize_session=False)
        else:
            session.query(models.User).filter(models.User.tid == tid).update({'password_change_needed': True}, synchronize_session=False)

    if disable_escrow:
        if tid == 1:
            session.query(models.User).update({'crypto_escrow_bkp1_key': ''}, synchronize_session=False)
        else:
            session.query(models.User).update({'crypto_escrow_bkp2_key': ''}, synchronize_session=False)

        session.query(models.User).filter(models.User.tid == tid).update({'crypto_escrow_prv_key': ''}, synchronize_session=False)

    config.set_val('crypto_escrow_pub_key', State.tenant_cache[tid].crypto_escrow_pub_key)

    db_refresh_memory_variables(session, [tid])

    if tid == 1:
        log.setloglevel(config.get_val('log_level'))

    return db_admin_serialize_node(session, tid, language)
Beispiel #3
0
def db_update_step(session, tid, step_id, request, language):
    """
    Transaction for updating a step

    :param session: An ORM session
    :param tid: The tenant ID
    :param step_id: the step_id of the step to update
    :param request: the step definition dict
    :param language: the language of the step definition dict
    :return: a serialization of the object
    """
    step = db_get(
        session, models.Step,
        (models.Step.id == step_id, models.Questionnaire.id
         == models.Step.questionnaire_id, models.Questionnaire.tid == tid))

    fill_localized_keys(request, models.Step.localized_keys, language)

    step.update(request)

    for child in request['children']:
        db_update_field(session, tid, child['id'], child, language)

    db_reset_option_triggers(session, 'step', step.id)

    for trigger in request.get('triggered_by_options', []):
        db_create_option_trigger(session, trigger['option'], 'step', step.id,
                                 trigger.get('sufficient', True))

    return serialize_step(session, tid, step, language)
Beispiel #4
0
def create_comment(session, tid, wbtip_id, content):
    wbtip, itip = db_get(session,
                         (models.WhistleblowerTip, models.InternalTip),
                         (models.WhistleblowerTip.id == wbtip_id,
                          models.InternalTip.id == models.WhistleblowerTip.id,
                          models.InternalTip.enable_two_way_comments.is_(True),
                          models.InternalTip.status != 'closed',
                          models.InternalTip.tid == tid))

    itip.update_date = itip.wb_last_access = datetime_now()

    _content = content
    if itip.crypto_tip_pub_key:
        _content = base64.b64encode(GCE.asymmetric_encrypt(itip.crypto_tip_pub_key, content)).decode()

    comment = models.Comment()
    comment.internaltip_id = wbtip_id
    comment.type = 'whistleblower'
    comment.content = _content
    session.add(comment)
    session.flush()

    ret = serialize_comment(session, comment)
    ret['content'] = content

    return ret
Beispiel #5
0
def perform_tips_operation(session, tid, receiver_id, operation, rtips_ids):
    """
    Transaction for performing operation on submissions (postpone/delete)

    :param session: An ORM session
    :param tid: A tenant ID
    :param receiver_id: A recipient ID
    :param operation: An operation command (postpone/delete)
    :param rtips_ids: The set of submissions on which performing the specified operation
    """
    receiver = db_get(session, models.User, models.User.id == receiver_id)

    can_postpone_expiration = State.tenant_cache[tid].can_postpone_expiration or receiver.can_postpone_expiration
    can_delete_submission = State.tenant_cache[tid].can_delete_submission or receiver.can_delete_submission

    itips = session.query(models.InternalTip) \
                   .filter(models.ReceiverTip.receiver_id == receiver_id,
                           models.ReceiverTip.id.in_(rtips_ids),
                           models.InternalTip.id == models.ReceiverTip.internaltip_id,
                           models.InternalTip.tid == tid)

    if operation == 'postpone' and can_postpone_expiration:
        for itip in itips:
            db_postpone_expiration(session, itip)

    elif operation == 'delete' and can_delete_submission:
        db_delete_itips(session, [itip.id for itip in itips])

    else:
        raise errors.ForbiddenOperation
Beispiel #6
0
def db_get_questionnaire(session, tid, questionnaire_id, language, serialize_templates=True):
    questionnaire = db_get(session,
                           models.Questionnaire,
                           (models.Questionnaire.tid.in_(set([1, tid])),
                            models.Questionnaire.id == questionnaire_id))

    return serialize_questionnaire(session, tid, questionnaire, language, serialize_templates=serialize_templates)
Beispiel #7
0
def update(session, tid, request):
    tenant = db_get(session, models.Tenant, models.Tenant.id == tid)

    tenant.active = request['active']

    for var in ['mode', 'name', 'subdomain']:
        db_set_config_variable(session, tid, var, request[var])

    return serialize_tenant(session, tenant)
Beispiel #8
0
def db_user_update_user(session, tid, user_session, request):
    """
    Transaction for updating an existing user

    :param session: An ORM session
    :param tid: A tenant ID
    :param user_session: A session of the user invoking the transaction
    :param request: A user request data
    :return: A user model
    """
    from globaleaks.handlers.admin.notification import db_get_notification
    from globaleaks.handlers.admin.node import db_admin_serialize_node

    user = db_get(session,
                  models.User,
                  models.User.id == user_session.user_id)

    user.language = request.get('language', State.tenant_cache[tid].default_language)
    user.name = request['name']
    user.public_name = request['public_name'] if request['public_name'] else request['name']

    if request['password']:
        if user.password_change_needed:
            user.password_change_needed = False
        else:
            if not GCE.check_password(user.hash_alg,
                                      request['old_password'],
                                      user.salt,
                                      user.password):
                raise errors.InvalidOldPassword

        user_session.cc = set_user_password(tid, user, request['password'], user_session.cc)

    # If the email address changed, send a validation email
    if request['mail_address'] != user.mail_address:
        user.change_email_address = request['mail_address']
        user.change_email_date = datetime_now()
        user.change_email_token = generateRandomKey()

        user_desc = user_serialize_user(session, user, user.language)

        user_desc['mail_address'] = request['mail_address']

        template_vars = {
            'type': 'email_validation',
            'user': user_desc,
            'new_email_address': request['mail_address'],
            'validation_token': user.change_email_token,
            'node': db_admin_serialize_node(session, tid, user.language),
            'notification': db_get_notification(session, tid, user.language)
        }

        State.format_and_send_mail(session, tid, user_desc, template_vars)

    parse_pgp_options(user, request)

    return user
Beispiel #9
0
def db_get_wbtip(session, itip_id, language):
    wbtip, itip = db_get(session,
                         (models.WhistleblowerTip, models.InternalTip),
                         (models.WhistleblowerTip.id == models.InternalTip.id,
                          models.InternalTip.id == itip_id))

    itip.wb_access_counter += 1
    itip.wb_last_access = datetime_now()

    return serialize_wbtip(session, wbtip, itip, language), base64.b64decode(wbtip.crypto_tip_prv_key)
Beispiel #10
0
def db_get_user(session, tid, user_id):
    """
    Transaction for retrieving a user model given an id

    :param session: An ORM session
    :param tid: A tenant ID
    :param user_id: A id of the user to retrieve
    :return: A retrieved model
    """
    return db_get(session, models.User,
                  (models.User.id == user_id, models.User.tid == tid))
Beispiel #11
0
def get_file_id(session, tid, name):
    """
    Transaction returning a file ID given the file name

    :param session: An ORM session
    :param tid: A tenant on which performing the lookup
    :param name: A file name
    :return: A result model
    """
    return db_get(session, models.File.id,
                  (models.File.tid == tid, models.File.name == name))[0]
Beispiel #12
0
    def download_wbfile(self, session, tid, file_id):
        wbfile, wbtip = db_get(session,
                               (models.WhistleblowerFile, models.WhistleblowerTip),
                               (models.WhistleblowerFile.id == file_id,
                                models.WhistleblowerFile.receivertip_id == models.ReceiverTip.id,
                                models.ReceiverTip.internaltip_id == models.WhistleblowerTip.id))

        if not self.user_can_access(session, tid, wbfile):
            raise errors.ResourceNotFound()

        self.access_wbfile(session, wbfile)

        return serializers.serialize_wbfile(session, wbfile), base64.b64decode(wbtip.crypto_tip_prv_key)
Beispiel #13
0
def db_update_field(session, tid, field_id, request, language):
    """
    Transaction for updating a field

    :param session: An ORM session
    :param tid: The tenant ID
    :param field_id: The ID of the object to be updated
    :param request: The request data
    :param language: The language of the request
    :return: The updated field
    """
    field = db_get(session,
                   models.Field,
                   (models.Field.tid == tid,
                    models.Field.id == field_id))

    check_field_association(session, tid, request)

    fill_localized_keys(request, models.Field.localized_keys, language)

    if field.instance != 'reference' or field.template_id == 'whistleblower_identity':
        db_update_fieldattrs(session, field.id, request['attrs'], language)

    db_reset_option_triggers(session, 'field', field.id)

    for trigger in request.get('triggered_by_options', []):
        db_create_option_trigger(session, trigger['option'], 'field', field.id, trigger.get('sufficient', True))

    if field.instance != 'reference':
        db_update_fieldoptions(session, field.id, request['options'], language)

        # full update
        field.update(request)

    else:
        # partial update
        field.update({
            'label': request['label'],
            'hint': request['hint'],
            'description': request['description'],
            'placeholder': request['placeholder'],
            'template_override_id': request['template_override_id'],
            'x': request['x'],
            'y': request['y'],
            'width': request['width'],
            'required': request['required']
        })

    return field
Beispiel #14
0
def db_access_rtip(session, tid, user_id, rtip_id):
    """
    Transaction retrieving an rtip and performing basic access checks

    :param session: An ORM session
    :param tid: A tenant ID of the user
    :param user_id: A user ID
    :param rtip_id: the requested rtip ID
    :return: A model requested
    """
    return db_get(
        session, (models.ReceiverTip, models.InternalTip),
        (models.ReceiverTip.id == rtip_id, models.ReceiverTip.receiver_id
         == user_id, models.ReceiverTip.internaltip_id
         == models.InternalTip.id, models.InternalTip.tid == tid))
Beispiel #15
0
def db_get_submission_status(session, tid, status_id, language):
    """
    Transaction for fetching the submission status given its ID

    :param session: An ORM session
    :param tid: A tenant ID
    :param status_id: The ID of the submission status to be retriven
    :param language: The language to be used in the serialization
    :return: The serialized descriptor of the indicated submission status
    """
    status = db_get(session, models.SubmissionStatus,
                    (models.SubmissionStatus.tid
                     == tid, models.SubmissionStatus.id == status_id))

    return serialize_submission_status(session, status, language)
Beispiel #16
0
def update_identity_information(session, tid, tip_id, identity_field_id, wbi, language):
    itip = db_get(session,
                  models.InternalTip,
                  (models.InternalTip.id == tip_id,
                   models.InternalTip.status != 'closed',
                   models.InternalTip.tid == tid))

    if itip.crypto_tip_pub_key:
        wbi = base64.b64encode(GCE.asymmetric_encrypt(itip.crypto_tip_pub_key, json.dumps(wbi).encode())).decode()

    db_set_internaltip_data(session, itip.id, 'whistleblower_identity', wbi)

    now = datetime_now()
    itip.update_date = now
    itip.wb_last_access = now
Beispiel #17
0
def db_update_submission_status(session, tid, status_id, request, language):
    """
    Transaction for updating a submission status

    :param session: An ORM session
    :param tid: The tenant ID
    :param status_id: The ID of the object to be updated
    :param request: The request data
    :param language: The language of the request
    :return: The serialized descriptor of the updated object
    """
    status = db_get(session, models.SubmissionStatus,
                    (models.SubmissionStatus.tid
                     == tid, models.SubmissionStatus.id == status_id))

    db_update_status_model_from_request(status, request, language)
Beispiel #18
0
def delete_field(session, tid, field_id):
    """
    Transaction to delete a field

    :param session: An ORM session
    :param tid: The tenant ID
    :param field_id: The id of the field to be deleted
    """
    field = db_get(session,
                   models.Field,
                   (models.Field.tid == tid,
                    models.Field.id == field_id))

    if field.instance == 'template' and session.query(models.Field).filter(models.Field.tid == tid, models.Field.template_id == field.id).count():
        raise errors.InputValidationError("Cannot remove the field template as it is used by one or more questionnaires")

    session.delete(field)
Beispiel #19
0
    def download_rfile(self, session, tid, user_id, file_id):
        rfile, rtip = db_get(
            session, (models.ReceiverFile, models.ReceiverTip),
            (models.ReceiverFile.id
             == file_id, models.ReceiverFile.receivertip_id
             == models.ReceiverTip.id, models.ReceiverTip.receiver_id
             == user_id, models.ReceiverTip.internaltip_id
             == models.InternalTip.id, models.InternalTip.tid == tid))

        log.debug("Download of file %s by receiver %s (%d)" %
                  (rfile.internalfile_id, rtip.receiver_id, rfile.downloads))

        rfile.last_access = datetime_now()
        rfile.downloads += 1

        return serializers.serialize_rfile(session, rfile), base64.b64decode(
            rtip.crypto_tip_prv_key)
Beispiel #20
0
    def download_wbfile(self, session, tid, file_id):
        wbfile, wbtip, = db_get(
            session, (models.WhistleblowerFile, models.WhistleblowerTip),
            (models.WhistleblowerFile.id == file_id,
             models.WhistleblowerFile.receivertip_id == models.ReceiverTip.id,
             models.ReceiverTip.internaltip_id == models.WhistleblowerTip.id))

        rtip = session.query(models.ReceiverTip) \
                      .filter(models.ReceiverTip.receiver_id == self.current_user.user_id,
                              models.ReceiverTip.internaltip_id == wbtip.id).one_or_none()
        if not rtip:
            raise errors.ResourceNotFound()

        self.access_wbfile(session, wbfile)

        return serializers.serialize_wbfile(session, wbfile), base64.b64decode(
            rtip.crypto_tip_prv_key)
Beispiel #21
0
def update_context(session, tid, context_id, request, language):
    """
    Transaction for updating a context

    :param session: An ORM session
    :param tid: The tenant ID
    :param context_id: The ID of object to be updated
    :param request: The request data
    :param language: The request language
    :return: A serialized descriptor of the context
    """
    context = db_get(
        session, models.Context,
        (models.Context.tid == tid, models.Context.id == context_id))
    context = db_update_context(session, tid, context, request, language)

    return admin_serialize_context(session, context, language)
Beispiel #22
0
def postpone_expiration(session, tid, user_id, rtip_id):
    """
    Transaction for postponing the expiration of a submission

    :param session: An ORM session
    :param tid: A tenant ID of the user performing the operation
    :param user_id: A user ID of the user performing the operation
    :param rtip_id: A rtip ID of the submission object of the operation
    """
    rtip, itip = db_access_rtip(session, tid, user_id, rtip_id)

    receiver = db_get(session, models.User, models.User.id == rtip.receiver_id)

    if not (State.tenant_cache[tid].can_postpone_expiration
            or receiver.can_postpone_expiration):
        raise errors.ForbiddenOperation

    db_postpone_expiration(session, itip)
Beispiel #23
0
def db_access_wbfile(session, tid, user_id, wbfile_id):
    """
    Transaction retrieving an wbfile and performing basic access checks

    :param session: An ORM session
    :param tid: A tenant ID of the user
    :param user_id: A user ID
    :param wbfile_id: the requested wbfile ID
    :return: A model requested
    """
    itips_ids = [x[0] for x in session.query(models.InternalTip.id) \
                                      .filter(models.InternalTip.id == models.ReceiverTip.internaltip_id,
                                              models.ReceiverTip.receiver_id == user_id,
                                              models.InternalTip.tid == tid)]

    return db_get(
        session, models.WhistleblowerFile,
        (models.WhistleblowerFile.id == wbfile_id,
         models.WhistleblowerFile.receivertip_id == models.ReceiverTip.id,
         models.ReceiverTip.internaltip_id.in_(itips_ids),
         models.InternalTip.tid == tid))
Beispiel #24
0
def db_update_questionnaire(session, tid, questionnaire_id, request, language):
    """
    Updates the specified questionnaire. If the key receivers is specified we remove
    the current receivers of the Questionnaire and reset set it to the new specified
    ones.

    :param session: An ORM session
    :param tid: A tenant ID
    :param questionnaire_id: The ID of the model to be updated
    :param request: The request data
    :param language: The language of the request
    :return: A serialized descriptor of the questionnaire
    """
    questionnaire = db_get(session, models.Questionnaire,
                           (models.Questionnaire.tid == tid,
                            models.Questionnaire.id == questionnaire_id))

    fill_localized_keys(request, models.Questionnaire.localized_keys, language)

    questionnaire.update(request)

    return serialize_questionnaire(session, tid, questionnaire, language)
Beispiel #25
0
def receiver_serialize_wbfile(session, wbfile):
    """
    Transaction returning a serialized descriptor of an wbfile

    :param session: An ORM session
    :param wbfile: A model to be serialized
    :return: A serialized description of the model specified
    """
    rtip = db_get(session, models.ReceiverTip,
                  models.ReceiverTip.id == wbfile.receivertip_id)

    return {
        'id': wbfile.id,
        'creation_date': wbfile.creation_date,
        'name': wbfile.name,
        'filename': wbfile.filename,
        'description': wbfile.description,
        'size': wbfile.size,
        'type': wbfile.content_type,
        'downloads': wbfile.downloads,
        'author': rtip.receiver_id,
        'path': os.path.join(Settings.attachments_path, wbfile.filename)
    }
Beispiel #26
0
def delete_rtip(session, tid, user_id, rtip_id):
    """
    Transaction for deleting a submission

    :param session: An ORM session
    :param tid: A tenant ID of the user performing the operation
    :param user_id: A user ID of the user performing the operation
    :param rtip_id: A rtip ID of the submission object of the operation
    """
    rtip, itip = db_access_rtip(session, tid, user_id, rtip_id)

    receiver = db_get(session, models.User, models.User.id == rtip.receiver_id)

    if not (State.tenant_cache[tid].can_delete_submission
            or receiver.can_delete_submission):
        raise errors.ForbiddenOperation

    State.log(tid=tid,
              type='delete_report',
              user_id=user_id,
              object_id=itip.id)

    db_delete_itip(session, itip.id)
def set_reset_token(session, user_id, validation_token):
    user = db_get(session, models.User, models.User.id == user_id)
    user.change_email_date = datetime_now()
    user.reset_password_token = validation_token
    user.reset_password_date = datetime_now()
Beispiel #28
0
def get(session, tid):
    return serialize_tenant(
        session, db_get(session, models.Tenant, models.Tenant.id == tid))
def set_email_token(session, user_id, validation_token, email):
    user = db_get(session, models.User, models.User.id == user_id)
    user.change_email_date = datetime_now()
    user.change_email_token = validation_token
    user.change_email_address = email
Beispiel #30
0
def db_create_submission(session, tid, request, token, client_using_tor):
    tenant = db_get(session, models.Tenant, models.Tenant.id == tid)

    context, questionnaire = db_get(
        session, (models.Context, models.Questionnaire),
        (models.Context.id == request['context_id'], models.Questionnaire.id
         == models.Context.questionnaire_id))

    receivers = request['receivers']
    answers = request['answers']
    steps = db_get_questionnaire(session, tid, questionnaire.id, None)['steps']
    questionnaire_hash = db_archive_questionnaire_schema(session, steps)
    preview = extract_answers_preview(steps, answers)

    crypto_is_available = State.tenant_cache[tid].encryption

    crypto_tip_pub_key = ''
    if crypto_is_available:
        crypto_tip_prv_key, crypto_tip_pub_key = GCE.generate_keypair()
        receivers = [r[0] for r in session.query(models.User.id) \
                                          .filter(models.User.id.in_(receivers),
                                                  models.User.crypto_pub_key != '')]

    if not request['receivers']:
        raise errors.InputValidationError(
            "Unable to deliver the submission to at least one recipient")

    if 0 < context.maximum_selectable_receivers < len(request['receivers']):
        raise errors.InputValidationError(
            "The number of recipients selected exceed the configured limit")

    itip = models.InternalTip()
    itip.tid = tid
    itip.status = 'new'
    itip.crypto_tip_pub_key = crypto_tip_pub_key

    itip.progressive = db_assign_submission_progressive(session, tid)

    itip.additional_questionnaire_id = context.additional_questionnaire_id

    if context.tip_timetolive > 0:
        itip.expiration_date = get_expiration(context.tip_timetolive)

    # Evaluate the score level
    itip.total_score = request['total_score']

    # The status https is used to keep track of the security level adopted by the whistleblower
    itip.https = not client_using_tor
    itip.mobile = request['mobile']

    itip.context_id = context.id
    itip.enable_two_way_comments = context.enable_two_way_comments
    itip.enable_two_way_messages = context.enable_two_way_messages
    itip.enable_attachments = context.enable_attachments

    x = session.query(models.Field, models.FieldAttr.value) \
               .filter(models.Field.template_id == 'whistleblower_identity',
                       models.Field.step_id == models.Step.id,
                       models.Step.questionnaire_id == context.questionnaire_id,
                       models.FieldAttr.field_id == models.Field.id,
                       models.FieldAttr.name == 'visibility_subject_to_authorization').one_or_none()

    whistleblower_identity = None
    can_access_whistleblower_identity = True

    if x:
        whistleblower_identity = x[0]
        can_access_whistleblower_identity = not x[1]

    itip.enable_whistleblower_identity = whistleblower_identity is not None

    session.add(itip)
    session.flush()

    # Evaluate if the whistleblower tip should be generated
    if ((not State.tenant_cache[tid].enable_scoring_system)
            or (context.score_threshold_receipt == 0)
            or (context.score_threshold_receipt == 1 and itip.total_score >= 2)
            or
        (context.score_threshold_receipt == 2 and itip.total_score == 3)):

        receipt = GCE.generate_receipt()
        receipt_salt = State.tenant_cache[tid].receipt_salt

        wbtip = models.WhistleblowerTip()
        wbtip.id = itip.id
        wbtip.tid = tid
        wbtip.hash_alg = 'ARGON2'
        wbtip.receipt_hash = GCE.hash_password(receipt, receipt_salt)

        # Evaluate if the whistleblower tip should be encrypted
        if crypto_is_available:
            crypto_tip_prv_key, itip.crypto_tip_pub_key = GCE.generate_keypair(
            )
            wb_key = GCE.derive_key(receipt.encode(), receipt_salt)
            wb_prv_key, wb_pub_key = GCE.generate_keypair()
            wbtip.crypto_prv_key = Base64Encoder.encode(
                GCE.symmetric_encrypt(wb_key, wb_prv_key))
            wbtip.crypto_pub_key = wb_pub_key
            wbtip.crypto_tip_prv_key = Base64Encoder.encode(
                GCE.asymmetric_encrypt(wb_pub_key, crypto_tip_prv_key))

        session.add(wbtip)
    else:
        receipt = ''

    # Apply special handling to the whistleblower identity question
    if itip.enable_whistleblower_identity and request[
            'identity_provided'] and answers[whistleblower_identity.id]:
        if crypto_is_available:
            wbi = base64.b64encode(
                GCE.asymmetric_encrypt(
                    itip.crypto_tip_pub_key,
                    json.dumps(answers[whistleblower_identity.id]
                               [0]).encode())).decode()
        else:
            wbi = answers[whistleblower_identity.id][0]

        answers[whistleblower_identity.id] = ''

        db_set_internaltip_data(session, itip.id, 'whistleblower_identity',
                                wbi)

    db_save_plaintext_answers(session, tid, itip.id, answers,
                              crypto_is_available)

    if crypto_is_available:
        preview = base64.b64encode(
            GCE.asymmetric_encrypt(
                itip.crypto_tip_pub_key,
                json.dumps(preview, cls=JSONEncoder).encode())).decode()
        answers = base64.b64encode(
            GCE.asymmetric_encrypt(
                itip.crypto_tip_pub_key,
                json.dumps(answers, cls=JSONEncoder).encode())).decode()

    itip.preview = preview

    db_set_internaltip_answers(session, itip.id, questionnaire_hash, answers)

    for uploaded_file in token.uploaded_files:
        if not itip.enable_attachments:
            break

        if uploaded_file['id'] in request['removed_files']:
            continue

        if crypto_is_available:
            for k in ['name', 'type', 'size']:
                uploaded_file[k] = base64.b64encode(
                    GCE.asymmetric_encrypt(itip.crypto_tip_pub_key,
                                           str(uploaded_file[k])))

        new_file = models.InternalFile()
        new_file.tid = tid
        new_file.name = uploaded_file['name']
        new_file.content_type = uploaded_file['type']
        new_file.size = uploaded_file['size']
        new_file.internaltip_id = itip.id
        new_file.filename = uploaded_file['filename']
        new_file.submission = uploaded_file['submission']
        session.add(new_file)

    for user in session.query(models.User).filter(
            models.User.id.in_(request['receivers'])):
        _tip_key = b''
        if crypto_is_available:
            if not user.crypto_pub_key:
                continue

            _tip_key = GCE.asymmetric_encrypt(user.crypto_pub_key,
                                              crypto_tip_prv_key)

        db_create_receivertip(session, user, itip,
                              can_access_whistleblower_identity, _tip_key)

    return {'receipt': receipt, 'score': itip.total_score}