def post(self): request = self.validate_message(self.request.content.read(), requests.AuthDesc) tid = int(request['tid']) if tid == 0: tid = self.request.tid yield login_delay(tid) self.state.tokens.use(request['token']) session = yield login(tid, request['username'], request['password'], request['authcode'], self.request.client_using_tor, self.request.client_ip) State.log(tid=tid, type='login', user_id=session.user_id) if tid != self.request.tid: returnValue({ 'redirect': 'https://%s/#/login?token=%s' % (State.tenant_cache[tid].hostname, session.id) }) returnValue(session.serialize())
def login_failure(tid, role, whistleblower=False): Settings.failed_login_attempts[tid] = Settings.failed_login_attempts.get( tid, 0) + 1 State.log(tid=tid, type='whistleblower_login_failure' if whistleblower else 'login_failure') raise errors.InvalidAuthentication
def delete(self): """ Logout """ if self.current_user.user_role == 'whistleblower': State.log(tid=self.current_user.tid, type='whistleblower_logout') else: State.log(tid=self.current_user.tid, type='logout', user_id=self.current_user.user_id) del Sessions[self.current_user.id]
def reset_submissions(session, tid, user_id): """ Transaction to reset the submissions of the specified tenant :param session: An ORM session :param tid: A tenant ID """ session.query(Config).filter(Config.tid == tid, Config.var_name == 'counter_submissions').update({'value': 0}) db_del(session, InternalTip, InternalTip.tid==tid) State.log(tid=tid, type='reset_reports', user_id=user_id)
def post(self): request = self.validate_message(self.request.content.read(), requests.ReceiptAuthDesc) yield login_delay(self.request.tid) self.state.tokens.use(request['token']) connection_check(self.request.tid, self.request.client_ip, 'whistleblower', self.request.client_using_tor) session = yield login_whistleblower(self.request.tid, request['receipt']) State.log(tid=session.tid, type='whistleblower_login') returnValue(session.serialize())
def set_user_password(tid, user, password, cc): # Regenerate the password hash only if different from the best choice on the platform if user.hash_alg != 'ARGON2': user.hash_alg = 'ARGON2' user.salt = GCE.generate_salt() password_hash = GCE.hash_password(password, user.salt) # Check that the new password is different form the current password if user.password == password_hash: raise errors.PasswordReuseError user.password = password_hash user.password_change_date = datetime_now() State.log(tid=tid, type='change_password', user_id=user.id, object_id=user.id) if not State.tenant_cache[tid].encryption and cc == '': return None enc_key = GCE.derive_key(password.encode(), user.salt) if not cc: # The first password change triggers the generation # of the user encryption private key and its backup cc, user.crypto_pub_key = GCE.generate_keypair() user.crypto_bkp_key, user.crypto_rec_key = GCE.generate_recovery_key( cc) user.crypto_prv_key = Base64Encoder.encode( GCE.symmetric_encrypt(enc_key, cc)) if State.tenant_cache[1].crypto_escrow_pub_key: user.crypto_escrow_bkp1_key = Base64Encoder.encode( GCE.asymmetric_encrypt(State.tenant_cache[1].crypto_escrow_pub_key, cc)) if State.tenant_cache[tid].crypto_escrow_pub_key: user.crypto_escrow_bkp2_key = Base64Encoder.encode( GCE.asymmetric_encrypt( State.tenant_cache[tid].crypto_escrow_pub_key, cc)) return cc
def db_admin_update_user(session, tid, user_session, user_id, request, language): """ Transaction for updating an existing user :param session: An ORM session :param tid: A tenant ID :param user_session: The current user session :param user_id: The ID of the user to update :param request: The request data :param language: The language of the request :return: The serialized descriptor of the updated object """ fill_localized_keys(request, models.User.localized_keys, language) user = db_get_user(session, tid, user_id) user.update(request) password = request['password'] if password and (not user.crypto_pub_key or user_session.ek): if user.crypto_pub_key and user_session.ek: enc_key = GCE.derive_key(password.encode(), user.salt) crypto_escrow_prv_key = GCE.asymmetric_decrypt(user_session.cc, Base64Encoder.decode(user_session.ek)) if user_session.user_tid == 1: user_cc = GCE.asymmetric_decrypt(crypto_escrow_prv_key, Base64Encoder.decode(user.crypto_escrow_bkp1_key)) else: user_cc = GCE.asymmetric_decrypt(crypto_escrow_prv_key, Base64Encoder.decode(user.crypto_escrow_bkp2_key)) user.crypto_prv_key = Base64Encoder.encode(GCE.symmetric_encrypt(enc_key, user_cc)) if user.hash_alg != 'ARGON2': user.hash_alg = 'ARGON2' user.salt = GCE.generate_salt() user.password = GCE.hash_password(password, user.salt) user.password_change_date = datetime_now() State.log(tid=tid, type='change_password', user_id=user_session.user_id, object_id=user_id) # The various options related in manage PGP keys are used here. parse_pgp_options(user, request) return user_serialize_user(session, user, language)
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: itip_ids = [itip.id for itip in itips] for itip_id in itip_ids: State.log(tid=tid, type='delete_report', user_id=receiver_id, object_id=itip_id) db_del(session, models.InternalTip, models.InternalTip.id.in_(itip_ids)) else: raise errors.ForbiddenOperation
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 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}