コード例 #1
0
def db_generate_password_reset_token(session, user):
    """
    Transaction for issuing password reset tokens

    :param session: An ORM session
    :param user: The user for which issuing a password reset token
    """
    user.reset_password_token = generateRandomKey()
    user.reset_password_date = datetime_now()

    if user.last_login > datetime_null():
        template = 'password_reset_validation'
    else:
        template = 'account_activation'

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

    template_vars = {
        'type': template,
        'user': user_desc,
        'reset_token': user.reset_password_token,
        'node': db_admin_serialize_node(session, user.tid, user.language),
        'notification': db_get_notification(session, user.tid, user.language)
    }

    State.format_and_send_mail(session, user.tid, user_desc, template_vars)
コード例 #2
0
ファイル: authentication.py プロジェクト: hevelius/GlobaLeaks
    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())
コード例 #3
0
ファイル: mailutils.py プロジェクト: zshell/GlobaLeaks
def mail_exception_handler(etype, value, tback):
    """
    Formats traceback and exception data and emails the error,
    This would be enabled only in the testing phase and testing release,
    not in production release.
    """
    if isinstance(value, (GeneratorExit,
                          defer.AlreadyCalledError,
                          SMTPError)) or \
        (etype == AssertionError and value.message == "Request closed"):
        # we need to bypass email notification for some exception that:
        # 1) raise frequently or lie in a twisted bug;
        # 2) lack of useful stacktraces;
        # 3) can be cause of email storm amplification
        #
        # this kind of exception can be simply logged error logs.
        log.err(
            "exception mail suppressed for exception (%s) [reason: special exception]",
            str(etype))
        return

    # collection of the stacktrace info
    exc_type = re.sub("(<(type|class ')|'exceptions.|'>|__main__.)", "",
                      str(etype))
    error_message = "%s %s" % (exc_type.strip(), etype.__doc__)
    traceinfo = '\n'.join(traceback.format_exception(etype, value, tback))

    mail_body = error_message + "\n\n" + traceinfo

    log.err("Unhandled exception raised:")
    log.err(mail_body)

    from globaleaks.state import State
    State.schedule_exception_email(mail_body)
コード例 #4
0
def login(session, tid, username, password, authcode, client_using_tor,
          client_ip):
    """
    login returns a session
    """
    user = None

    users = session.query(User).filter(User.username == username,
                                       User.state != u'disabled',
                                       UserTenant.user_id == User.id,
                                       UserTenant.tenant_id == tid).distinct()
    for u in users:
        if GCE.check_password(u.hash_alg, password, u.salt, u.password):
            user = u
            break

    if user is None:
        log.debug("Login: Invalid credentials")
        Settings.failed_login_attempts += 1
        raise errors.InvalidAuthentication

    connection_check(client_ip, tid, user.role, client_using_tor)

    if State.tenant_cache[
            1].two_factor_auth and user.last_login != datetime_null():
        token = TwoFactorTokens.get(user.id)

        if token is not None and authcode != '':
            if token.token == authcode:
                TwoFactorTokens.revoke(user.id)
            else:
                raise errors.InvalidTwoFactorAuthCode

        elif token is None and authcode == '':
            token = TwoFactorTokens.new(user.id)

            data = {'type': '2fa', 'authcode': str(token.token)}

            data['node'] = db_admin_serialize_node(session, tid, user.language)
            data['notification'] = db_get_notification(session, tid,
                                                       user.language)

            subject, body = Templating().get_mail_subject_and_body(data)
            State.sendmail(1, user.mail_address, subject, body)
            raise errors.TwoFactorAuthCodeRequired
        else:
            raise errors.TwoFactorAuthCodeRequired

    user.last_login = datetime_now()

    crypto_prv_key = ''
    if State.tenant_cache[1].encryption and user.crypto_prv_key:
        user_key = GCE.derive_key(password.encode('utf-8'), user.salt)
        crypto_prv_key = GCE.symmetric_decrypt(user_key, user.crypto_prv_key)

    return Sessions.new(tid, user.id, user.role, user.password_change_needed,
                        crypto_prv_key)
コード例 #5
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 = models.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
コード例 #6
0
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
コード例 #7
0
ファイル: authentication.py プロジェクト: hevelius/GlobaLeaks
    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]
コード例 #8
0
    def process_mail_creation(self, store, data):
        user_id = data['user']['id']

        # Do not spool emails if the receiver has opted out of ntfns for this tip.
        if not data['tip']['enable_notifications']:
            log.debug("Discarding emails for %s due to receiver's preference.",
                      user_id)
            return

        # https://github.com/globaleaks/GlobaLeaks/issues/798
        # TODO: the current solution is global and configurable only by the admin
        sent_emails = State.get_mail_counter(user_id)
        if sent_emails >= State.tenant_cache[
                1].notif.notification_threshold_per_hour:
            log.debug(
                "Discarding emails for receiver %s due to threshold already exceeded for the current hour",
                user_id)
            return

        State.increment_mail_counter(user_id)
        if sent_emails >= State.tenant_cache[
                1].notif.notification_threshold_per_hour:
            log.info(
                "Reached threshold of %d emails with limit of %d for receiver %s",
                sent_emails,
                State.tenant_cache[1].notif.notification_threshold_per_hour,
                user_id)

            # simply changing the type of the notification causes
            # to send the notification_limit_reached
            data['type'] = u'receiver_notification_limit_reached'

        data['notification'] = self.serialize_config(store, 'notification',
                                                     data['user']['language'])
        data['node'] = self.serialize_config(store, 'node',
                                             data['user']['language'])

        if not data['node']['allow_unencrypted'] and len(
                data['user']['pgp_key_public']) == 0:
            return

        subject, body = Templating().get_mail_subject_and_body(data)

        # If the receiver has encryption enabled encrypt the mail body
        if data['user']['pgp_key_public']:
            body = encrypt_message(data['user']['pgp_key_public'], body)

        store.add(
            models.Mail({
                'address': data['user']['mail_address'],
                'subject': subject,
                'body': body
            }))
コード例 #9
0
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)
コード例 #10
0
ファイル: authentication.py プロジェクト: hevelius/GlobaLeaks
    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())
コード例 #11
0
ファイル: user.py プロジェクト: hevelius/GlobaLeaks
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
コード例 #12
0
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)
コード例 #13
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:
        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
コード例 #14
0
ファイル: helpers.py プロジェクト: prateekj117/GlobaLeaks
def init_state():
    Settings.set_devel_mode()
    Settings.disable_notifications = True
    Settings.failed_login_attempts.clear()
    Settings.working_path = os.path.abspath('./working_path')

    Settings.eval_paths()

    if os.path.exists(Settings.working_path):
        shutil.rmtree(Settings.working_path)

    orm.set_thread_pool(FakeThreadPool())

    State.settings.enable_api_cache = False
    State.tenant_cache[1] = ObjectDict()
    State.tenant_cache[1].hostname = 'www.globaleaks.org'
    State.tenant_cache[1].encryption = True

    State.init_environment()

    Sessions.clear()
コード例 #15
0
ファイル: helpers.py プロジェクト: 0xjukai/GlobaLeaks
def init_state():
    Settings.testing = True
    Settings.set_devel_mode()
    Settings.logging = None
    Settings.failed_login_attempts = 0
    Settings.working_path = './working_path'

    Settings.eval_paths()

    if os.path.exists(Settings.working_path):
        dir_util.remove_tree(Settings.working_path, 0)

    orm.set_thread_pool(FakeThreadPool())

    State.settings.enable_api_cache = False
    State.tenant_cache[1] = ObjectDict()
    State.tenant_cache[1].hostname = 'www.globaleaks.org'

    State.init_environment()

    Sessions.clear()
コード例 #16
0
ファイル: helpers.py プロジェクト: globaleaks/GlobaLeaks
def init_state():
    Settings.testing = True
    Settings.set_devel_mode()
    Settings.logging = None
    Settings.failed_login_attempts = 0
    Settings.working_path = os.path.abspath('./working_path')

    Settings.eval_paths()

    if os.path.exists(Settings.working_path):
        dir_util.remove_tree(Settings.working_path, 0)

    orm.set_thread_pool(FakeThreadPool())

    State.settings.enable_api_cache = False
    State.tenant_cache[1] = ObjectDict()
    State.tenant_cache[1].hostname = 'www.globaleaks.org'
    State.tenant_cache[1].encryption = True

    State.init_environment()

    Sessions.clear()
コード例 #17
0
ファイル: helpers.py プロジェクト: vondrakk/GlobaLeaks
    def setUp(self):
        test_config.skipCase(self)
        self.test_reactor = task.Clock()

        jobs.base.reactor = self.test_reactor
        tempdict.reactor = self.test_reactor
        token.TokenList.reactor = self.test_reactor
        Sessions.reactor = self.test_reactor

        init_glsettings_for_unit_tests()

        self.setUp_dummy()

        if self.initialize_test_database_using_archived_db:
            shutil.copy(
                os.path.join(TEST_DIR, 'db', 'empty', Settings.db_file_name),
                os.path.join(Settings.working_path, 'db',
                             Settings.db_file_name))
        else:
            yield db.init_db()

        allow_unencrypted = self.encryption_scenario in ['PLAINTEXT', 'MIXED']

        yield update_node_setting(u'allow_unencrypted', allow_unencrypted)

        yield db.refresh_memory_variables()

        sup = ProcessSupervisor([], '127.0.0.1', 8082)
        State.process_supervisor = sup

        Alarm.reset()
        event.EventTrackQueue.clear()
        State.reset_hourly()

        Settings.submission_minimum_delay = 0

        self.internationalized_text = load_appdata(
        )['node']['whistleblowing_button']
コード例 #18
0
    def operation(self):
        # ------- BEGIN Anomalies section -------
        anomalies_to_save = get_anomalies()
        if anomalies_to_save:
            save_anomalies(anomalies_to_save)
            log.debug("Stored %d anomalies collected during the last hour",
                      len(anomalies_to_save))

        # ------- END Anomalies section ---------

        # ------- BEGIN Stats section -----------
        current_time = datetime_now()
        statistic_summary = get_statistics()
        if statistic_summary:
            save_statistics(State.stats_collection_start_time, current_time,
                            statistic_summary)
            log.debug("Stored statistics %s collected from %s to %s",
                      statistic_summary, State.stats_collection_start_time,
                      current_time)
        # ------- END Stats section -------------

        # Hourly Resets
        State.reset_hourly()
コード例 #19
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)
コード例 #20
0
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}
コード例 #21
0
ファイル: signup.py プロジェクト: pabit/GlobaLeaks
def signup(session, request, language):
    """
    Transact handling the registration of a new signup

    :param session: An ORM session
    :param request: A user request
    :param language: A language of the request
    """
    config = ConfigFactory(session, 1)

    if not config.get_val('enable_signup'):
        raise errors.ForbiddenOperation

    request['activation_token'] = generateRandomKey(32)
    request['language'] = language

    # Delete the tenants created for the same subdomain that have still not been activated
    # Ticket reference: https://github.com/globaleaks/GlobaLeaks/issues/2640
    subquery = session.query(models.Tenant.id) \
                      .filter(models.Signup.subdomain == request['subdomain'],
                              not_(models.Signup.activation_token.is_(None)),
                              models.Tenant.id == models.Signup.tid,
                              models.Tenant.subdomain == models.Signup.subdomain) \
                      .subquery()

    session.query(models.Tenant).filter(
        models.Tenant.id.in_(subquery)).delete(synchronize_session=False)

    tenant_id = db_preallocate_tenant(session, {
        'label': request['subdomain'],
        'subdomain': request['subdomain']
    }).id

    signup = models.Signup(request)

    signup.tid = tenant_id

    session.add(signup)

    # We need to send two emails
    #
    # The first one is sent to the platform owner with the activation email.
    #
    # The second goes to the instance administrators notifying them that a new
    # platform has been added.

    signup_dict = serialize_signup(signup)

    # Email 1 - Activation Link
    template_vars = {
        'type': 'signup',
        'node': db_admin_serialize_node(session, 1, language),
        'notification': db_get_notification(session, 1, language),
        'signup': signup_dict
    }

    State.format_and_send_mail(session, 1, {'mail_address': signup.email},
                               template_vars)

    # Email 2 - Admin Notification
    for user_desc in db_get_users(session, 1, 'admin'):
        template_vars = {
            'type': 'admin_signup_alert',
            'node': db_admin_serialize_node(session, 1, user_desc['language']),
            'notification': db_get_notification(session, 1,
                                                user_desc['language']),
            'user': user_desc,
            'signup': signup_dict
        }

        State.format_and_send_mail(session, 1, user_desc, template_vars)
コード例 #22
0
ファイル: signup.py プロジェクト: pabit/GlobaLeaks
def signup_activation(session, token, hostname, language):
    """
    Transaction registering the activation of a platform registered via signup

    :param session: An ORM session
    :param token: A activation token
    :param language: A language of the request
    """
    config = ConfigFactory(session, 1)

    if not config.get_val('enable_signup'):
        raise errors.ForbiddenOperation

    ret = session.query(models.Signup, models.Tenant) \
                 .filter(models.Signup.activation_token == token,
                         models.Tenant.id == models.Signup.tid).one_or_none()

    if ret is None:
        return {}

    signup, tenant = ret[0], ret[1]

    signup.activation_token = None

    mode = config.get_val('mode')

    db_initialize_tenant(session, tenant, mode)

    password_admin = generateRandomKey(16)
    password_receiver = generateRandomKey(16)

    node_name = signup.organization_name if signup.organization_name else signup.subdomain

    wizard = {
        'node_language': signup.language,
        'node_name': node_name,
        'admin_username': '******',
        'admin_name': signup.name + ' ' + signup.surname,
        'admin_password': password_admin,
        'admin_mail_address': signup.email,
        'receiver_username': '******',
        'receiver_name': signup.name + ' ' + signup.surname,
        'receiver_password': password_receiver,
        'receiver_mail_address': signup.email,
        'profile': 'default',
        'skip_recipient_account_creation': False,
        'enable_developers_exception_notification': True
    }

    db_wizard(session, signup.tid, hostname, wizard)

    template_vars = {
        'type': 'activation',
        'node': db_admin_serialize_node(session, 1, language),
        'notification': db_get_notification(session, 1, language),
        'signup': serialize_signup(signup),
        'password_admin': wizard['admin_password'],
        'password_recipient': wizard['receiver_password']
    }

    State.format_and_send_mail(session, 1, {'mail_address': signup.email},
                               template_vars)

    db_refresh_memory_variables(session, [signup.tid])
コード例 #23
0
ファイル: authentication.py プロジェクト: chojar/GlobaLeaks
def login(session, tid, username, password, authcode, client_using_tor, client_ip):
    """
    login returns a session
    """
    user = None

    users = session.query(User).filter(User.username == username,
                                       User.state != u'disabled',
                                       UserTenant.user_id == User.id,
                                       UserTenant.tenant_id == tid).distinct()
    for u in users:
        if GCE.check_password(u.hash_alg, password, u.salt, u.password):
            user = u
            break

        # Fix for issue: https://github.com/globaleaks/GlobaLeaks/issues/2563
        if State.tenant_cache[1].creation_date < datetime.timestamp(datetime(2019, 5, 3, 0, 0)):
            u_password = '******'' + u.password + '\''
            if GCE.check_password(u.hash_alg, password, u.salt, u_password):
                user = u
                break

    if user is None:
        log.debug("Login: Invalid credentials")
        Settings.failed_login_attempts += 1
        raise errors.InvalidAuthentication

    connection_check(client_ip, tid, user.role, client_using_tor)

    if State.tenant_cache[1].two_factor_auth and user.last_login != datetime_null():
        token = TwoFactorTokens.get(user.id)

        if token is not None and authcode != '':
            if token.token == authcode:
                TwoFactorTokens.revoke(user.id)
            else:
                raise errors.InvalidTwoFactorAuthCode

        elif token is None and authcode == '':
            token = TwoFactorTokens.new(user.id)

            data = {
                'type': '2fa',
                'authcode': str(token.token)
            }

            data['node'] = db_admin_serialize_node(session, tid, user.language)
            data['notification'] = db_get_notification(session, tid, user.language)

            subject, body = Templating().get_mail_subject_and_body(data)
            State.sendmail(1, user.mail_address, subject, body)
            raise errors.TwoFactorAuthCodeRequired
        else:
            raise errors.TwoFactorAuthCodeRequired

    user.last_login = datetime_now()

    crypto_prv_key = ''
    if State.tenant_cache[1].encryption and user.crypto_prv_key:
        user_key = GCE.derive_key(password.encode('utf-8'), user.salt)
        crypto_prv_key = GCE.symmetric_decrypt(user_key, user.crypto_prv_key)

    return Sessions.new(tid, user.id, user.role, user.password_change_needed, crypto_prv_key)
コード例 #24
0
def login(session,
          tid,
          username,
          password,
          receiver_second_login,
          receiver_auth_code,
          client_using_tor,
          client_ip,
          token=''):
    """
    login returns a tuple (user_id, state, role, pcn, authCodePrepared)
    """
    if token:
        user = session.query(User).filter(User.auth_token == token, \
                                          User.state != u'disabled', \
                                          User.tid == tid).one_or_none()
    else:
        user = session.query(User).filter(User.username == username, \
                                          User.state != u'disabled', \
                                          User.tid == tid).one_or_none()

    if user is None or (not token and not security.check_password(
            password, user.salt, user.password)):
        log.debug("Login: Invalid credentials")
        Settings.failed_login_attempts += 1
        raise errors.InvalidAuthentication

    if not client_using_tor and not mystate.tenant_cache[tid]['https_' +
                                                              user.role]:
        log.err("Denied login request over Web for role '%s'" % user.role)
        raise errors.TorNetworkRequired

    # Check if we're doing IP address checks today
    if mystate.tenant_cache[tid]['ip_filter_authenticated_enable']:
        ip_networks = parse_csv_ip_ranges_to_ip_networks(
            mystate.tenant_cache[tid]['ip_filter_authenticated'])
        client_ip = text_type(client_ip)
        client_ip_obj = ipaddress.ip_address(client_ip)

        # Safety check, we always allow localhost to log in
        success = False
        if client_ip_obj.is_loopback is True:
            success = True

        for ip_network in ip_networks:
            if client_ip_obj in ip_network:
                success = True

        if success is not True:
            raise errors.AccessLocationInvalid

    # se sono arrivato qui il primo login è andato a buon fine
    # il login (username, password) per un ricevente viene rieseguito anche al secondo passaggio
    # per motivi di sicurezza
    # A QUESTO PUNTO:
    # SE receiver2ndStepLoginState = 'N':
    # 1 - genero il codice
    # 2 - memorizzo record in ReceiverAuthCode
    # 3 - invio mail
    # ELSE:
    # verifico la correttenza del secondo codice
    if user.role == 'receiver':

        receiver = session.query(Receiver).filter(
            Receiver.id == user.id).one_or_none()

        if receiver.two_step_login_enabled and not user.password_change_needed:

            if receiver_second_login == 'first_login_to_complete':

                yyyy = str(datetime_now().year)
                mm = str(datetime_now().month).zfill(2)
                dd = str(datetime_now().day).zfill(2)
                result_query = session.query(ReceiverAuthCode).filter(
                    ReceiverAuthCode.receiver_id == user.id,
                    func.strftime("%Y-%m-%d",
                                  ReceiverAuthCode.creation_date) == yyyy +
                    '-' + mm + '-' + dd).all()

                # genero il codice
                #chiamo la funzione generate_authcode_password che genera la password con l'utilizzo della funzione os.urandom
                #randnum = ''.join(["%s" % SystemRandom().randint(0, 9) for num in range(0, 12)])
                randnum = generate_authcode_password(12)

                #print randnum
                log.debug(randnum[0:4] + ' ' + randnum[4:8] + ' ' +
                          randnum[8:12])

                # inserisco il record nel db
                newAuthCode = models.ReceiverAuthCode()
                newAuthCode.receiver_id = receiver.id

                newAuthCode.salt = security.generateRandomSalt()
                newAuthCode.auth_code = security.hash_password(
                    randnum, newAuthCode.salt)

                session.add(newAuthCode)
                session.flush()

                # invio i tre pezzi del codice alle tre mail specificate nel profilo del ricevente
                email_prg = str(len(result_query) + 1)
                day = dd + '/' + mm + '/' + yyyy
                mystate.sendmail(
                    1, receiver.control_mail_1,
                    "Receiver Auth Code #" + email_prg + " - " + day,
                    randnum[0:4])
                mystate.sendmail(
                    1, receiver.control_mail_2,
                    "Receiver Auth Code #" + email_prg + " - " + day,
                    randnum[4:8])
                mystate.sendmail(
                    1, receiver.control_mail_3,
                    "Receiver Auth Code #" + email_prg + " - " + day,
                    randnum[8:12])

                log.debug("Invio delle mail effettuato con successo")

                receiver_second_login = '******'

            elif receiver_second_login == 'second_login_to_complete':
                auth_code_item = session.query(ReceiverAuthCode).filter(ReceiverAuthCode.receiver_id == user.id) \
                                                                .order_by(ReceiverAuthCode.creation_date.desc()).first()

                # se non sono passati TOT minuti dall'ultimo codice emesso si può controllare la validità del codice
                if auth_code_item is not None and auth_code_item.is_valid and not is_expired(
                        auth_code_item.creation_date, 0,
                        AUTH_CODE_EXPIRATION_IN_MINUTES, 0, 0):

                    # qui devo verificare che il codice inviato dall'utente sia uguale a una delle permutazioni dei tre blocchi
                    # da quattro cifre che si ottengono dal codice salvato sul db
                    firstBlock = receiver_auth_code[0:4]
                    secondBlock = receiver_auth_code[4:8]
                    thirdBlock = receiver_auth_code[8:12]
                    combList = []
                    combList.insert(0, firstBlock + secondBlock + thirdBlock)
                    combList.insert(1, firstBlock + thirdBlock + secondBlock)
                    combList.insert(2, secondBlock + firstBlock + thirdBlock)
                    combList.insert(3, secondBlock + thirdBlock + firstBlock)
                    combList.insert(4, thirdBlock + firstBlock + secondBlock)
                    combList.insert(5, thirdBlock + secondBlock + firstBlock)

                    auth_code_match = False
                    for authCode in combList:
                        if security.check_password(authCode,
                                                   auth_code_item.salt,
                                                   auth_code_item.auth_code):
                            auth_code_match = True

                            # POSSO ANCHE FARE UNA UPDATE del campo is_valid PER IL RECORD UTILIZZATO NELLA VALIDAZIONE
                            auth_code_item.is_valid = False
                            session.add(auth_code_item)
                            session.flush()

                            #objs = session.query(ReceiverAuthCode).filter(ReceiverAuthCode.receiver_id == auth_code_item.receiver_id) \
                            #                                      .order_by(ReceiverAuthCode.creation_date.desc(), ReceiverAuthCode.daily_prg.desc())
                            #for obj in objs:
                            #    session.delete(obj)
                            #    session.flush()
                            #session.delete(auth_code_item)

                            receiver_second_login = '******'
                            break

                    if not auth_code_match:
                        log.debug("Login: Invalid authentication code")
                        Settings.failed_login_attempts += 1
                        raise errors.InvalidAuthentication

                else:
                    log.debug(
                        "Login: authentication code is expired. Please repeat login"
                    )
                    Settings.failed_login_attempts += 1
                    raise errors.InvalidAuthentication

            else:
                log.debug(
                    "receiver_auth_code diverso da first_login_to_complete e second_login_to_complete"
                )
                receiver_second_login = '******'
        else:
            receiver_second_login = '******'  #da tenere sotto controllo

    else:
        receiver_second_login = '******'  # da tenere sotto controllo

    log.debug("Login: Success (%s)" % user.role)

    user.last_login = datetime_now()

    return user.id, user.state, user.role, user.password_change_needed, receiver_second_login
コード例 #25
0
ファイル: user.py プロジェクト: CreativeOthman/GlobaLeaks
def db_user_update_user(session, tid, user_session, request):
    """
    Updates the specified user.
    This version of the function is specific for users that with comparison with
    admins can change only few things:
      - real name
      - email address
      - preferred language
      - the password (with old password check)
      - pgp key
    raises: globaleaks.errors.ResourceNotFound` if the receiver does not exist.
    """
    from globaleaks.handlers.admin.notification import db_get_notification
    from globaleaks.handlers.admin.node import db_admin_serialize_node

    user = models.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']
    new_password = request['password']
    old_password = request['old_password']

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

        # Regenerate the password hash only if different from the best choice on the platform
        if user.hash_alg != GCE.HASH:
            user.hash_alg = GCE.HASH
            user.salt = GCE.generate_salt()

        password_hash = GCE.hash_password(new_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()

        if State.tenant_cache[tid].encryption:
            enc_key = GCE.derive_key(request['password'].encode(), user.salt)
            if not user_session.cc:
                # Th First first password change triggers the generation
                # of the user encryption private key and its backup
                user_session.cc, user.crypto_pub_key = GCE.generate_keypair()
                user.crypto_bkp_key, user.crypto_rec_key = GCE.generate_recovery_key(
                    user_session.cc)

                # If the user had already enabled two factor before encryption was not enable
                # encrypt the two factor secret
                if user.two_factor_secret:
                    user.two_factor_secret = GCE.asymmetric_encrypt(
                        user.crypto_pub_key, user.two_factor_secret)

            user.crypto_prv_key = GCE.symmetric_encrypt(
                enc_key, 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(32)

        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)

    # If the platform allows users to change PGP keys, process it
    if State.tenant_cache[tid]['enable_user_pgp_key_upload'] is True:
        parse_pgp_options(user, request)

    return user
コード例 #26
0
ファイル: user.py プロジェクト: chojar/GlobaLeaks
def db_user_update_user(session, tid, user_session, request):
    """
    Updates the specified user.
    This version of the function is specific for users that with comparison with
    admins can change only few things:
      - real name
      - email address
      - preferred language
      - the password (with old password check)
      - pgp key
    raises: globaleaks.errors.ResourceNotFound` if the receiver does not exist.
    """
    from globaleaks.handlers.admin.notification import db_get_notification
    from globaleaks.handlers.admin.node import db_admin_serialize_node

    user = models.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']
    new_password = request['password']
    old_password = request['old_password']

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

        user.hash_alg = GCE.HASH
        user.salt = GCE.generate_salt()
        user.password = GCE.hash_password(new_password, user.salt)
        user.password_change_date = datetime_now()

        if State.tenant_cache[1].encryption:
            enc_key = GCE.derive_key(request['password'].encode(), user.salt)
            if not user_session.cc:
                user_session.cc, user.crypto_pub_key = GCE.generate_keypair()

            user.crypto_prv_key = GCE.symmetric_encrypt(enc_key, 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(32)

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

        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, 1, user.language),
            'notification': db_get_notification(session, tid, user.language)
        }

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

    # If the platform allows users to change PGP keys, process it
    if State.tenant_cache[tid]['enable_user_pgp_key_upload'] is True:
        parse_pgp_options(user, request)

    return user