def register_ifile_on_db(session, tid, internaltip_id, uploaded_file): now = datetime_now() itip = session.query(models.InternalTip) \ .filter(models.InternalTip.id == internaltip_id, models.InternalTip.tid == tid).one() itip.update_date = now itip.wb_last_access = now if itip.crypto_tip_pub_key: for k in ['name', 'type', 'size']: uploaded_file[k] = base64.b64encode( GCE.asymmetric_encrypt(itip.crypto_tip_pub_key, uploaded_file[k])) new_file = models.InternalFile() new_file.name = uploaded_file['name'] new_file.content_type = uploaded_file['type'] new_file.size = uploaded_file['size'] new_file.internaltip_id = internaltip_id new_file.filename = uploaded_file['filename'] new_file.submission = uploaded_file['submission'] new_file.internaltip_id = internaltip_id session.add(new_file) return serializers.serialize_ifile(session, new_file)
def register_ifile_on_db(store, uploaded_file, internaltip_id): internaltip = models.db_get(store, models.InternalTip, id=internaltip_id) internaltip.update_date = datetime_now() new_file = models.InternalFile() new_file.name = uploaded_file['name'] new_file.content_type = uploaded_file['type'] new_file.size = uploaded_file['size'] new_file.internaltip_id = internaltip_id new_file.submission = uploaded_file['submission'] new_file.file_path = uploaded_file['path'] store.add(new_file) return serializers.serialize_ifile(store, new_file)
def register_ifile_on_db(session, tid, uploaded_file, internaltip_id): now = datetime_now() session.query(models.InternalTip) \ .filter(models.InternalTip.id == internaltip_id, models.InternalTip.tid == tid) \ .update({'update_date': now, 'wb_last_access': now}) 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 = internaltip_id new_file.submission = uploaded_file['submission'] new_file.filename = uploaded_file['filename'] session.add(new_file) return serializers.serialize_ifile(session, new_file)
def register_ifile_on_db(session, tid, internaltip_id, uploaded_file): """ Register a file on the database :param session: An ORM session :param tid: A tenant id :param internaltip_id: A id of the submission on which attaching the file :param uploaded_file: A file to be attached :return: A descriptor of the file """ now = datetime_now() itip = session.query(models.InternalTip) \ .filter(models.InternalTip.id == internaltip_id, models.InternalTip.enable_attachments.is_(True), models.InternalTip.status != 'closed', models.InternalTip.tid == tid).one() itip.update_date = now itip.wb_last_access = now if itip.crypto_tip_pub_key: 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.name = uploaded_file['name'] new_file.content_type = uploaded_file['type'] new_file.size = uploaded_file['size'] new_file.internaltip_id = internaltip_id new_file.filename = uploaded_file['filename'] new_file.submission = uploaded_file['submission'] new_file.internaltip_id = internaltip_id session.add(new_file) return serializers.serialize_ifile(session, new_file)
def db_create_submission(store, request, uploaded_files, client_using_tor, language): answers = request['answers'] context = store.find(models.Context, models.Context.id == request['context_id']).one() if not context: raise errors.ContextIdNotFound submission = models.InternalTip() submission.progressive = db_assign_submission_progressive(store) if context.tip_timetolive > -1: submission.expiration_date = get_expiration(context.tip_timetolive) else: submission.expiration_date = datetime_never() # this is get from the client as it the only possibility possible # that would fit with the end to end submission. # the score is only an indicator and not a critical information so we can accept to # be fooled by the malicious user. submission.total_score = request['total_score'] # The status tor2web is used to keep track of the security level adopted by the whistleblower submission.tor2web = not client_using_tor submission.context_id = context.id submission.enable_two_way_comments = context.enable_two_way_comments submission.enable_two_way_messages = context.enable_two_way_messages submission.enable_attachments = context.enable_attachments submission.enable_whistleblower_identity = context.questionnaire.enable_whistleblower_identity if submission.enable_whistleblower_identity and request[ 'identity_provided']: submission.identity_provided = True submission.identity_provided_date = datetime_now() try: questionnaire = db_get_context_steps(store, context.id, None) questionnaire_hash = unicode(sha256(json.dumps(questionnaire))) submission.questionnaire_hash = questionnaire_hash submission.preview = extract_answers_preview(questionnaire, answers) store.add(submission) db_archive_questionnaire_schema(store, questionnaire, questionnaire_hash) db_save_questionnaire_answers(store, submission.id, answers) except Exception as excep: log.err("Submission create: fields validation fail: %s" % excep) raise excep try: for filedesc in uploaded_files: new_file = models.InternalFile() new_file.name = filedesc['name'] new_file.description = "" new_file.content_type = filedesc['type'] new_file.size = filedesc['size'] new_file.internaltip_id = submission.id new_file.submission = filedesc['submission'] new_file.file_path = filedesc['path'] store.add(new_file) log.debug("=> file associated %s|%s (%d bytes)" % (new_file.name, new_file.content_type, new_file.size)) except Exception as excep: log.err("Submission create: unable to create db entry for files: %s" % excep) raise excep receipt, wbtip = db_create_whistleblowertip(store, submission) if submission.context.maximum_selectable_receivers > 0 and \ len(request['receivers']) > submission.context.maximum_selectable_receivers: raise errors.SubmissionValidationFailure( "selected an invalid number of recipients") rtips = [] for receiver in store.find(models.Receiver, In(models.Receiver.id, request['receivers'])): if submission.context not in receiver.contexts: continue if not GLSettings.memory_copy.allow_unencrypted and len( receiver.user.pgp_key_public) == 0: continue rtips.append(db_create_receivertip(store, receiver, submission)) if len(rtips) == 0: raise errors.SubmissionValidationFailure("need at least one recipient") log.debug("The finalized submission had created %d models.ReceiverTip(s)" % len(rtips)) submission_dict = serialize_usertip(store, wbtip, language) submission_dict.update({'receipt': receipt}) return submission_dict
def db_create_submission(session, tid, request, token, client_using_tor): if not request['receivers']: raise errors.InputValidationError("need at least one recipient") answers = request['answers'] context, questionnaire = session.query(models.Context, models.Questionnaire) \ .filter(models.Context.id == request['context_id'], models.Questionnaire.id == models.Context.questionnaire_id, models.Questionnaire.tid.in_(set([1, tid]))).one_or_none() if not context: raise errors.ModelNotFound(models.Context) steps = db_get_questionnaire(session, tid, questionnaire.id, None)['steps'] questionnaire_hash = db_archive_questionnaire_schema(session, steps) itip = models.InternalTip() itip.tid = tid itip.status = db_get_id_for_system_status(session, tid, u'new') 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) # this is get from the client as it the only possibility possible # that would fit with the end to end submission. # the score is only an indicator and not a critical information so we can accept to # be fooled by the malicious user. 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.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 == u'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 == u'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 itip.preview = extract_answers_preview(steps, answers) session.add(itip) session.flush() 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 = GCE.HASH wbtip.receipt_hash = GCE.hash_password(receipt, receipt_salt) crypto_is_available = State.tenant_cache[1].encryption if crypto_is_available: users_count = session.query(models.User) \ .filter(models.User.id.in_(request['receivers']), models.User.crypto_prv_key != b'').count() crypto_is_available = users_count == len(request['receivers']) 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 = GCE.symmetric_encrypt(wb_key, wb_prv_key) wbtip.crypto_pub_key = wb_pub_key wbtip.crypto_tip_prv_key = GCE.asymmetric_encrypt( wb_pub_key, crypto_tip_prv_key) if itip.enable_whistleblower_identity and request[ 'identity_provided'] and answers[whistleblower_identity.id]: wbi = answers[whistleblower_identity.id][0] answers[whistleblower_identity.id] = '' if crypto_is_available: wbi = base64.b64encode( GCE.asymmetric_encrypt(itip.crypto_tip_pub_key, json.dumps(wbi).encode())).decode() db_set_internaltip_data(session, itip.id, 'identity_provided', True, False) db_set_internaltip_data(session, itip.id, 'whistleblower_identity', wbi, crypto_is_available) if crypto_is_available: answers = base64.b64encode( GCE.asymmetric_encrypt(itip.crypto_tip_pub_key, json.dumps(answers).encode())).decode() else: db_save_questionnaire_answers(session, tid, itip.id, answers) db_set_internaltip_answers(session, itip.id, questionnaire_hash, answers, crypto_is_available) session.add(wbtip) for filedesc in token.uploaded_files: new_file = models.InternalFile() new_file.tid = tid new_file.encrypted = crypto_is_available new_file.name = filedesc['name'] new_file.description = "" new_file.content_type = filedesc['type'] new_file.size = filedesc['size'] new_file.internaltip_id = itip.id new_file.submission = filedesc['submission'] new_file.filename = filedesc['filename'] session.add(new_file) log.debug("=> file associated %s|%s (%d bytes)", new_file.name, new_file.content_type, new_file.size) if context.maximum_selectable_receivers > 0 and \ len(request['receivers']) > context.maximum_selectable_receivers: raise errors.InputValidationError( "selected an invalid number of recipients") for user in session.query(models.User).filter( models.User.id.in_(request['receivers'])): if not crypto_is_available and not user.pgp_key_public and not State.tenant_cache[ tid].allow_unencrypted: continue _tip_key = b'' if crypto_is_available: _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}
def db_create_submission(session, tid, request, token, client_using_tor): answers = request['answers'] context, questionnaire = session.query(models.Context, models.Questionnaire) \ .filter(models.Context.id == request['context_id'], models.Questionnaire.id == models.Context.questionnaire_id, models.Questionnaire.tid.in_(set([1, tid]))).one_or_none() if not context: raise errors.ModelNotFound(models.Context) if not request['receivers']: raise errors.InputValidationError("The submission should involve at least one recipient") if context.maximum_selectable_receivers > 0 and \ len(request['receivers']) > context.maximum_selectable_receivers: raise errors.InputValidationError("The number of recipients selected exceed the configured limit") steps = db_get_questionnaire(session, tid, questionnaire.id, None)['steps'] questionnaire_hash = db_archive_questionnaire_schema(session, steps) preview = extract_answers_preview(steps, answers) itip = models.InternalTip() itip.tid = tid itip.status = 'new' 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() crypto_is_available = State.tenant_cache[tid].encryption # Evaluate if encryption is available if crypto_is_available: users_count = session.query(models.User) \ .filter(models.User.id.in_(request['receivers']), models.User.crypto_prv_key != b'').count() crypto_is_available = users_count == len(request['receivers']) if crypto_is_available: crypto_tip_prv_key, itip.crypto_tip_pub_key = GCE.generate_keypair() # 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 = GCE.symmetric_encrypt(wb_key, wb_prv_key) wbtip.crypto_pub_key = wb_pub_key wbtip.crypto_tip_prv_key = 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) if crypto_is_available: preview = base64.b64encode(GCE.asymmetric_encrypt(itip.crypto_tip_pub_key, json.dumps(preview).encode())).decode() answers = base64.b64encode(GCE.asymmetric_encrypt(itip.crypto_tip_pub_key, json.dumps(answers).encode())).decode() itip.preview = preview db_set_internaltip_answers(session, itip.id, questionnaire_hash, answers) db_save_answers_subject_to_stats(session, tid, itip.id, answers) for uploaded_file in token.uploaded_files: 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, 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) log.debug("=> file associated %s|%s (%d bytes)", new_file.name, new_file.content_type, new_file.size) tip_count = 0 for user in session.query(models.User).filter(models.User.id.in_(request['receivers'])): _tip_key = b'' if crypto_is_available: _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) tip_count +=1 if not tip_count: raise errors.InputValidationError("Unable to deliver the submission to at least one recipient") return { 'receipt': receipt, 'score': itip.total_score }
def db_create_submission(store, token_id, request, t2w, language): # the .get method raise an exception if the token is invalid token = TokenList.get(token_id) token.use() answers = request['answers'] context = store.find(models.Context, models.Context.id == request['context_id']).one() if not context: raise errors.ContextIdNotFound submission = models.InternalTip() submission.progressive = db_assign_submission_progressive(store) submission.expiration_date = utc_future_date(seconds=context.tip_timetolive) # this is get from the client as it the only possibility possible # that would fit with the end to end submission. # the score is only an indicator and not a critical information so we can accept to # be fooled by the malicious user. submission.total_score = request['total_score'] # The use of Tor2Web is detected by the basehandler and the status forwared here; # The status is used to keep track of the security level adopted by the whistleblower submission.tor2web = t2w submission.context_id = context.id submission.enable_two_way_comments = context.enable_two_way_comments submission.enable_two_way_messages = context.enable_two_way_messages submission.enable_attachments = context.enable_attachments submission.enable_whistleblower_identity = context.questionnaire.enable_whistleblower_identity if submission.enable_whistleblower_identity and request['identity_provided']: submission.identity_provided = True submission.identity_provided_date = datetime_now() try: questionnaire = db_get_context_steps(store, context.id, None) questionnaire_hash = unicode(sha256(json.dumps(questionnaire))) submission.questionnaire_hash = questionnaire_hash submission.preview = extract_answers_preview(questionnaire, answers) store.add(submission) db_archive_questionnaire_schema(store, questionnaire, questionnaire_hash) db_save_questionnaire_answers(store, submission.id, answers) except Exception as excep: log.err("Submission create: fields validation fail: %s" % excep) raise excep try: import_receivers(store, submission, request['receivers']) except Exception as excep: log.err("Submission create: receivers import fail: %s" % excep) raise excep try: for filedesc in token.uploaded_files: new_file = models.InternalFile() new_file.name = filedesc['filename'] new_file.description = "" new_file.content_type = filedesc['content_type'] new_file.size = filedesc['body_len'] new_file.internaltip_id = submission.id new_file.submission = filedesc['submission'] new_file.file_path = filedesc['encrypted_path'] store.add(new_file) log.debug("=> file associated %s|%s (%d bytes)" % ( new_file.name, new_file.content_type, new_file.size)) except Exception as excep: log.err("Submission create: unable to create db entry for files: %s" % excep) raise excep receipt, wbtip = db_create_whistleblower_tip(store, submission) submission_dict = serialize_usertip(store, wbtip, language) submission_dict.update({'receipt': receipt}) return submission_dict
def db_create_submission(session, tid, request, uploaded_files, client_using_tor): answers = request['answers'] context, questionnaire = session.query(models.Context, models.Questionnaire) \ .filter(models.Context.id == request['context_id'], models.Questionnaire.id == models.Context.questionnaire_id, models.Questionnaire.tid.in_(set([1, tid]))).one_or_none() if not context: raise errors.ModelNotFound(models.Context) steps = db_get_questionnaire(session, tid, questionnaire.id, None)['steps'] questionnaire_hash = text_type(sha256(json.dumps(steps))) db_archive_questionnaire_schema(session, steps, questionnaire_hash) submission = models.InternalTip() submission.tid = tid submission.status = db_get_id_for_system_status(session, tid, 'new') submission.progressive = db_assign_submission_progressive(session, tid) if context.tip_timetolive > 0: submission.expiration_date = get_expiration(context.tip_timetolive) else: submission.expiration_date = datetime_never() # this is get from the client as it the only possibility possible # that would fit with the end to end submission. # the score is only an indicator and not a critical information so we can accept to # be fooled by the malicious user. submission.total_score = request['total_score'] # The status https is used to keep track of the security level adopted by the whistleblower submission.https = not client_using_tor submission.context_id = context.id submission.enable_two_way_comments = context.enable_two_way_comments submission.enable_two_way_messages = context.enable_two_way_messages submission.enable_attachments = context.enable_attachments whistleblower_identity = session.query(models.Field) \ .filter(models.Field.template_id == u'whistleblower_identity', models.Field.step_id == models.Step.id, models.Step.questionnaire_id == context.questionnaire_id).one_or_none() submission.enable_whistleblower_identity = whistleblower_identity is not None if submission.enable_whistleblower_identity and request['identity_provided']: submission.identity_provided = True submission.identity_provided_date = datetime_now() submission.questionnaire_hash = questionnaire_hash submission.preview = extract_answers_preview(steps, answers) session.add(submission) session.flush() receipt = text_type(generateRandomReceipt()) wbtip = models.WhistleblowerTip() wbtip.id = submission.id wbtip.tid = submission.tid wbtip.receipt_hash = hash_password(receipt, State.tenant_cache[tid].receipt_salt) session.add(wbtip) db_save_questionnaire_answers(session, tid, submission.id, answers) for filedesc in uploaded_files: new_file = models.InternalFile() new_file.tid = tid new_file.name = filedesc['name'] new_file.description = "" new_file.content_type = filedesc['type'] new_file.size = filedesc['size'] new_file.internaltip_id = submission.id new_file.submission = filedesc['submission'] new_file.filename = filedesc['filename'] session.add(new_file) log.debug("=> file associated %s|%s (%d bytes)", new_file.name, new_file.content_type, new_file.size) if context.maximum_selectable_receivers > 0 and \ len(request['receivers']) > context.maximum_selectable_receivers: raise errors.InputValidationError("selected an invalid number of recipients") rtips_count = 0 for receiver, user in session.query(models.Receiver, models.User) \ .filter(models.Receiver.id.in_(request['receivers']), models.ReceiverContext.receiver_id == models.Receiver.id, models.ReceiverContext.context_id == context.id, models.User.id == models.Receiver.id, models.UserTenant.user_id == models.User.id, models.UserTenant.tenant_id == tid): if user.pgp_key_public or State.tenant_cache[tid].allow_unencrypted: db_create_receivertip(session, receiver, submission) rtips_count += 1 if rtips_count == 0: raise errors.InputValidationError("need at least one recipient") log.debug("The finalized submission had created %d models.ReceiverTip(s)", rtips_count) return {'receipt': receipt}
def db_create_submission(store, request, uploaded_files, client_using_tor): answers = request['answers'] context, questionnaire = store.find( (models.Context, models.Questionnaire), models.Context.id == request['context_id'], models.Questionnaire.id == models.Context.questionnaire_id).one() if not context: raise errors.ModelNotFound(models.Context) steps = db_get_questionnaire(store, questionnaire.id, None)['steps'] questionnaire_hash = unicode(sha256(json.dumps(steps))) submission = models.InternalTip() submission.progressive = db_assign_submission_progressive(store) if context.tip_timetolive > -1: submission.expiration_date = get_expiration(context.tip_timetolive) else: submission.expiration_date = datetime_never() # this is get from the client as it the only possibility possible # that would fit with the end to end submission. # the score is only an indicator and not a critical information so we can accept to # be fooled by the malicious user. submission.total_score = request['total_score'] # The status tor2web is used to keep track of the security level adopted by the whistleblower submission.tor2web = not client_using_tor submission.context_id = context.id submission.enable_two_way_comments = context.enable_two_way_comments submission.enable_two_way_messages = context.enable_two_way_messages submission.enable_attachments = context.enable_attachments submission.enable_whistleblower_identity = questionnaire.enable_whistleblower_identity if submission.enable_whistleblower_identity and request[ 'identity_provided']: submission.identity_provided = True submission.identity_provided_date = datetime_now() submission.questionnaire_hash = questionnaire_hash submission.preview = extract_answers_preview(steps, answers) store.add(submission) db_archive_questionnaire_schema(store, steps, questionnaire_hash) db_save_questionnaire_answers(store, submission.id, answers) for filedesc in uploaded_files: new_file = models.InternalFile() new_file.name = filedesc['name'] new_file.description = "" new_file.content_type = filedesc['type'] new_file.size = filedesc['size'] new_file.internaltip_id = submission.id new_file.submission = filedesc['submission'] new_file.file_path = filedesc['path'] store.add(new_file) log.debug("=> file associated %s|%s (%d bytes)", new_file.name, new_file.content_type, new_file.size) receipt = db_create_whistleblowertip(store, submission) if context.maximum_selectable_receivers > 0 and \ len(request['receivers']) > context.maximum_selectable_receivers: raise errors.SubmissionValidationFailure( "selected an invalid number of recipients") rtips_count = 0 for receiver, user, in store.find( (models.Receiver, models.User), In(models.Receiver.id, request['receivers']), models.ReceiverContext.receiver_id == models.Receiver.id, models.ReceiverContext.context_id == context.id, models.User.id == models.Receiver.id): if user.pgp_key_public or GLSettings.memory_copy.allow_unencrypted: db_create_receivertip(store, receiver, submission) rtips_count += 1 if rtips_count == 0: raise errors.SubmissionValidationFailure("need at least one recipient") log.debug("The finalized submission had created %d models.ReceiverTip(s)", rtips_count) return {'receipt': receipt}
def db_create_submission(session, tid, request, token, client_using_tor): encryption = db_get( session, models.Config, (models.Config.tid == tid, models.Config.var_name == 'encryption')) crypto_is_available = encryption.value 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)) answers = request['answers'] steps = db_get_questionnaire(session, tid, questionnaire.id, None)['steps'] questionnaire_hash = db_archive_questionnaire_schema(session, steps) crypto_tip_pub_key = '' receivers = [] for r in session.query(models.User).filter( models.User.id.in_(request['receivers'])): if crypto_is_available: if r.crypto_pub_key: # This is the regular condition of systems setup on Globaleaks 4 # Since this version, encryption is enabled by default and # users need to perform their first access before they # could receive reports. receivers.append(r) elif encryption.update_date != datetime_null(): # This is the exceptional condition of systems setup when # encryption was implemented via PGP. # For continuity reason of those production systems # encryption could not be enforced. receivers.append(r) crypto_is_available = False else: receivers.append(r) if not 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") if crypto_is_available: crypto_tip_prv_key, crypto_tip_pub_key = GCE.generate_keypair() 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) 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) if crypto_is_available: answers = base64.b64encode( GCE.asymmetric_encrypt( itip.crypto_tip_pub_key, json.dumps(answers, cls=JSONEncoder).encode())).decode() 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 receivers: if crypto_is_available: _tip_key = GCE.asymmetric_encrypt(user.crypto_pub_key, crypto_tip_prv_key) else: _tip_key = b'' db_create_receivertip(session, user, itip, can_access_whistleblower_identity, _tip_key) State.log(tid=tid, type='whistleblower_new_report') return {'receipt': receipt, 'score': itip.total_score}